3 // Written by Harald JOHNSEN, started April 2005.
5 // Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
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.
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.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24 # include <simgear_config.h>
27 #include <osg/ref_ptr>
28 #include <osg/Texture2D>
30 #include <simgear/compiler.h>
33 #include <simgear/math/sg_random.h>
34 #include <simgear/misc/sg_path.hxx>
36 #include STL_ALGORITHM
39 #include "cloudfield.hxx"
40 #include "newcloud.hxx"
46 static osg::ref_ptr<osg::Texture2D> cloudTextures[SGNewCloud::CLTexture_max];
49 bool SGNewCloud::useAnisotropic = true;
50 SGBbCache *SGNewCloud::cldCache = 0;
51 static bool texturesLoaded = false;
52 static float minx, maxx, miny, maxy, minz, maxz;
53 static int cloudIdCounter = 1;
55 float SGNewCloud::nearRadius = 3500.0f;
56 bool SGNewCloud::lowQuality = false;
57 sgVec3 SGNewCloud::sunlight = {0.5f, 0.5f, 0.5f};
58 sgVec3 SGNewCloud::ambLight = {0.5f, 0.5f, 0.5f};
59 sgVec3 SGNewCloud::modelSunDir = {0,1,0};
62 void SGNewCloud::init(void) {
69 familly = CLFamilly_nn;
70 cloudId = ++cloudIdCounter;
71 sgSetVec3(center, 0.0f, 0.0f, 0.0f);
72 sgSetVec3(cloudpos, 0.0f, 0.0f, 0.0f);
75 list_spriteContainer.reserve(8);
76 list_spriteDef.reserve(40);
79 cldCache = new SGBbCache;
85 SGNewCloud::SGNewCloud(CLFamilly_type classification)
88 familly = classification;
91 SGNewCloud::SGNewCloud(string classification)
94 if( classification == "cu" )
95 familly = CLFamilly_cu;
96 else if( classification == "cb" )
97 familly = CLFamilly_cb;
98 else if( classification == "st" )
99 familly = CLFamilly_st;
100 else if( classification == "ns" )
101 familly = CLFamilly_ns;
102 else if( classification == "sc" )
103 familly = CLFamilly_sc;
104 else if( classification == "as" )
105 familly = CLFamilly_as;
106 else if( classification == "ac" )
107 familly = CLFamilly_ac;
108 else if( classification == "ci" )
109 familly = CLFamilly_ci;
110 else if( classification == "cc" )
111 familly = CLFamilly_cc;
112 else if( classification == "cs" )
113 familly = CLFamilly_cs;
116 SGNewCloud::~SGNewCloud() {
117 list_spriteDef.clear();
118 list_spriteContainer.clear();
119 cldCache->free( bbId, cloudId );
123 // load all textures used to draw cloud sprites
124 void SGNewCloud::loadTextures(const string &tex_path) {
127 texturesLoaded = true;
131 cloud_path.set(tex_path);
132 cloud_path.append("cl_cumulus.rgb");
134 // cloudTextures[ CLTexture_cumulus ] = new osg::Texture2D( cloud_path.str().c_str(), false, false, false );
135 cloudTextures[ CLTexture_cumulus ] = new osg::Texture2D;
137 cloud_path.set(tex_path);
138 cloud_path.append("cl_stratus.rgb");
140 // cloudTextures[ CLTexture_stratus ] = new ssgTexture( cloud_path.str().c_str(), false, false, false );
141 cloudTextures[ CLTexture_stratus ] = new osg::Texture2D;
145 void SGNewCloud::startFade(bool direction, float duration, float pauseLength) {
146 if(duration <= 0.0) {
150 this->direction = direction;
152 this->duration = duration;
153 this->pauseLength = pauseLength;
157 void SGNewCloud::setFade(float howMuch) {
165 static inline float rayleighCoeffAngular(float cosAngle) {
166 return 3.0f / 4.0f * (1.0f + cosAngle * cosAngle);
169 // cp is normalized (len==1)
170 static void CartToPolar3d(sgVec3 cp, sgVec3 polar) {
171 polar[0] = atan2(cp[1], cp[0]);
172 polar[1] = SG_PI / 2.0f - atan2(sqrt (cp[0] * cp[0] + cp[1] * cp[1]), cp[2]);
176 static void PolarToCart3d(sgVec3 p, sgVec3 cart) {
177 float tmp = cos(p[1]);
178 cart[0] = cos(p[0]) * tmp;
179 cart[1] = sin(p[0]) * tmp;
184 // compute the light for a cloud sprite corner
185 // from the normal and the sun, scaled by the Rayleigh factor
186 // and finaly added to the ambient light
187 static inline void lightFunction(sgVec3 normal, sgVec4 light, float pf) {
188 float cosAngle = sgScalarProductVec3( normal, SGNewCloud::modelSunDir);
189 float vl = (1.0f - 0.5f + cosAngle * 0.5f) * pf;
190 sgScaleVec3( light, SGNewCloud::sunlight, 0.25f + 0.75f * vl );
191 sgAddVec3( light, SGNewCloud::ambLight );
192 // we need to clamp or else the light will bug when adding transparency
193 if( light[0] > 1.0 ) light[0] = 1.0;
194 if( light[1] > 1.0 ) light[1] = 1.0;
195 if( light[2] > 1.0 ) light[2] = 1.0;
199 // compute the light for a cloud sprite
200 // we use ambient light and orientation versus sun position
201 void SGNewCloud::computeSimpleLight(sgVec3 FakeEyePos) {
202 // constant Rayleigh factor if we are not doing Anisotropic lighting
205 list_of_spriteDef::iterator iSprite;
206 for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
207 if( useAnisotropic ) {
209 sgSubVec3(eyeDir, iSprite->pos, FakeEyePos);
210 sgNormaliseVec3(eyeDir);
211 float cosAngle = sgScalarProductVec3(eyeDir, modelSunDir);
212 pf = rayleighCoeffAngular(cosAngle);
214 lightFunction(iSprite->n0, iSprite->l0, pf);
215 lightFunction(iSprite->n1, iSprite->l1, pf);
216 lightFunction(iSprite->n2, iSprite->l2, pf);
217 lightFunction(iSprite->n3, iSprite->l3, pf);
223 // add a new box to the cloud
224 void SGNewCloud::addContainer (float x, float y, float z, float r, CLbox_type type) {
225 spriteContainer cont;
226 sgSetVec3( cont.pos, x, y, z );
228 cont.cont_type = type;
229 sgSetVec3( cont.center, 0.0f, 0.0f, 0.0f);
230 list_spriteContainer.push_back( cont );
231 // don't place cloud below his base
232 if( y - r*0.50 < delta_base )
233 delta_base = y - r*0.50;
236 // add a sprite inside a box
237 void SGNewCloud::addSprite(float x, float y, float z, float r, CLbox_type type, int box) {
238 spriteDef newSpriteDef;
239 int rank = list_spriteDef.size();
240 sgSetVec3( newSpriteDef.pos, x, y - delta_base, z);
241 newSpriteDef.box = box;
242 newSpriteDef.sprite_type = type;
243 newSpriteDef.rank = rank;
245 list_spriteDef.push_back( newSpriteDef );
246 spriteContainer *thisBox = &list_spriteContainer[box];
248 sgSubVec3( deltaPos, newSpriteDef.pos, thisBox->pos );
249 sgAddVec3( thisBox->center, deltaPos );
251 r = r * 0.70f; // 0.5 * 1.xxx
267 // return a random number between -n/2 and n/2
268 static float Rnd(float n) {
269 return n * (-0.5f + sg_random());
272 // generate all sprite with defined boxes
273 void SGNewCloud::genSprites( void ) {
276 minx = miny = minz = 99999.0;
277 maxx = maxy = maxz = -99999.0;
279 N = list_spriteContainer.size();
280 for(int i = 0 ; i < N ; i++ ) {
281 spriteContainer *thisBox = & list_spriteContainer[i];
282 // the type defines how the sprites can be positioned inside the box, their size, etc
283 switch(thisBox->cont_type) {
286 r = thisBox->r + Rnd(0.2f);
287 x = thisBox->pos[SG_X] + Rnd(thisBox->r * 0.75f);
288 y = thisBox->pos[SG_Y] + Rnd(thisBox->r * 0.75f);
289 z = thisBox->pos[SG_Z] + Rnd(thisBox->r * 0.75f);
290 addSprite(x, y, z, r, thisBox->cont_type, i);
295 x = thisBox->pos[SG_X];
296 y = thisBox->pos[SG_Y];
297 z = thisBox->pos[SG_Z];
298 addSprite(x, y, z, r, thisBox->cont_type, i);
301 for( sc = 0 ; sc <= 4 ; sc ++ ) {
302 r = thisBox->r + Rnd(0.2f);
303 x = thisBox->pos[SG_X] + Rnd(thisBox->r * 0.75f);
304 y = thisBox->pos[SG_Y] + Rnd(thisBox->r * 0.5f);
305 z = thisBox->pos[SG_Z] + Rnd(thisBox->r * 0.75f);
306 if ( y < thisBox->pos[SG_Y] - thisBox->r / 10.0f )
307 y = thisBox->pos[SG_Y] - thisBox->r / 10.0f;
308 addSprite(x, y, z, r, thisBox->cont_type, i);
312 for( sc = 0 ; sc <= 4 ; sc ++ ) {
313 r = thisBox->r + Rnd(0.2f);
314 x = thisBox->pos[SG_X] + Rnd(thisBox->r);
315 y = thisBox->pos[SG_Y] + Rnd(thisBox->r);
316 z = thisBox->pos[SG_Z] + Rnd(thisBox->r);
317 addSprite(x, y, z, r, thisBox->cont_type, i);
321 sgScaleVec3(thisBox->center, 1.0f / sc);
324 radius = maxx - minx;
325 if ( (maxy - miny) > radius )
326 radius = (maxy - miny);
327 if ( (maxz - minz) > radius )
328 radius = (maxz - minz);
330 sgSetVec3( center, (maxx + minx) / 2.0f, (maxy + miny) / 2.0f, (maxz + minz) / 2.0f );
332 const float ang = 45.0f * SG_PI / 180.0f;
335 list_of_spriteDef::iterator iSprite;
336 for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
338 spriteContainer *thisSpriteContainer = &list_spriteContainer[iSprite->box];
339 if( familly == CLFamilly_sc || familly == CLFamilly_cu || familly == CLFamilly_cb) {
340 sgSubVec3(normal, iSprite->pos, center);
342 sgSubVec3(normal, iSprite->pos, thisSpriteContainer->pos);
343 sgSubVec3(normal, thisSpriteContainer->center);
344 sgSubVec3(normal, cloudpos);
346 if( normal[0] == 0.0f && normal[1] == 0.0f && normal[2] == 0.0f )
347 sgSetVec3( normal, 0.0f, 1.0f, 0.0f );
348 sgNormaliseVec3(normal);
349 // use exotic lightning function, this will give more 'relief' to the clouds
350 // compute a normal for each vextex this will simulate a smooth shading for a round shape
352 // I suspect this code to be bugged...
353 CartToPolar3d(normal, polar);
354 sgCopyVec3(iSprite->normal, normal);
356 // offset the normal vector by some angle for each vertex
357 sgSetVec3(pt, polar[0] - ang, polar[1] - ang, polar[2]);
358 PolarToCart3d(pt, iSprite->n0);
359 sgSetVec3(pt, polar[0] + ang, polar[1] - ang, polar[2]);
360 PolarToCart3d(pt, iSprite->n1);
361 sgSetVec3(pt, polar[0] + ang, polar[1] + ang, polar[2]);
362 PolarToCart3d(pt, iSprite->n2);
363 sgSetVec3(pt, polar[0] - ang, polar[1] + ang, polar[2]);
364 PolarToCart3d(pt, iSprite->n3);
367 // experimental : clouds are dissipating with time
368 if( familly == CLFamilly_cu ) {
369 startFade(true, 300.0f, 30.0f);
370 fadetimer = sg_random() * 300.0f;
375 // definition of a cu cloud, only for testing
376 void SGNewCloud::new_cu(void) {
378 float r = Rnd(1.0) + 0.5;
380 addContainer(0.0f, 0.0f, 0.0f, s, CLbox_cumulus);
381 addContainer(s, 0, 0, s, CLbox_cumulus);
382 addContainer(0, 0, 2 * s, s, CLbox_cumulus);
383 addContainer(s, 0, 2 * s, s, CLbox_cumulus);
385 addContainer(-1.2f * s, 0.2f * s, s, s * 1.4f, CLbox_cumulus);
386 addContainer(0.2f * s, 0.2f * s, s, s * 1.4f, CLbox_cumulus);
387 addContainer(1.6f * s, 0.2f * s, s, s * 1.4f, CLbox_cumulus);
388 } else if ( r < 0.90f ) {
389 addContainer(0, 0, 0, s * 1.2, CLbox_cumulus);
390 addContainer(s, 0, 0, s, CLbox_cumulus);
391 addContainer(0, 0, s, s, CLbox_cumulus);
392 addContainer(s * 1.1, 0, s, s * 1.2, CLbox_cumulus);
394 addContainer(-1.2 * s, 1 + 0.2 * s, s * 0.5, s * 1.4, CLbox_standard);
395 addContainer(0.2 * s, 1 + 0.25 * s, s * 0.5, s * 1.5, CLbox_standard);
396 addContainer(1.6 * s, 1 + 0.2 * s, s * 0.5, s * 1.4, CLbox_standard);
401 addContainer(0, 0, 0, s, CLbox_cumulus);
402 addContainer(0, 0, s, s, CLbox_cumulus);
403 addContainer(s, 0, s, s, CLbox_cumulus);
404 addContainer(s, 0, 0, s, CLbox_cumulus);
406 addContainer(s / 2, s, s / 2, s * 1.5, CLbox_standard);
408 addContainer(0, 2 * s, 0, s, CLbox_standard);
409 addContainer(0, 2 * s, s, s, CLbox_standard);
410 addContainer(s, 2 * s, s, s, CLbox_standard);
411 addContainer(s, 2 * s, 0, s, CLbox_standard);
418 // define the new position of the cloud (inside the cloud field, not on sphere)
419 void SGNewCloud::SetPos(sgVec3 newPos) {
420 int N = list_spriteDef.size();
422 sgSubVec3( deltaPos, newPos, cloudpos );
425 for(int i = 0 ; i < N ; i ++) {
426 sgAddVec3( list_spriteDef[i].pos, deltaPos );
428 sgAddVec3( center, deltaPos );
429 sgSetVec3( cloudpos, newPos[SG_X], newPos[SG_Y], newPos[SG_Z]);
435 void SGNewCloud::drawContainers() {
442 // sort on distance to eye because of transparency
443 void SGNewCloud::sortSprite( sgVec3 eye ) {
444 list_of_spriteDef::iterator iSprite;
446 // compute distance from sprite to eye
447 for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
449 sgSubVec3( dist, iSprite->pos, eye );
450 iSprite->dist = -(dist[0]*dist[0] + dist[1]*dist[1] + dist[2]*dist[2]);
452 std::sort( list_spriteDef.begin(), list_spriteDef.end() );
455 // render the cloud on screen or on the RTT texture to build the impostor
456 void SGNewCloud::Render3Dcloud( bool drawBB, sgVec3 FakeEyePos, sgVec3 deltaPos, float dist_center ) {
458 float step = ( list_spriteDef.size() * (direction ? fadetimer : duration-fadetimer)) / duration;
459 int clrank = (int) step;
460 float clfadeinrank = (step - clrank);
463 float CloudVisFade = 1.0 / (0.7f * SGCloudField::get_CloudVis());
464 // blend clouds with sky based on distance to limit the contrast of distant cloud
465 float t = 1.0f - dist_center * CloudVisFade;
469 computeSimpleLight( FakeEyePos );
471 // view point sort, we sort because of transparency
472 sortSprite( FakeEyePos );
474 float dark = (familly == CLFamilly_cb ? 0.9f : 1.0f);
476 GLint previousTexture = -1, thisTexture;
477 list_of_spriteDef::iterator iSprite;
478 for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
479 // skip this sprite if faded
480 if(iSprite->rank > clrank)
482 // choose texture to use depending on sprite type
483 switch(iSprite->sprite_type) {
485 thisTexture = CLTexture_stratus;
488 thisTexture = CLTexture_cumulus;
491 // in practice there is no texture switch (atm)
492 if( previousTexture != thisTexture ) {
493 previousTexture = thisTexture;
495 // glBindTexture(GL_TEXTURE_2D, cloudTextures[thisTexture]->getHandle());
499 sgSubVec3( translate, iSprite->pos, deltaPos);
502 // flipx and flipy are random texture flip flags, this gives more random clouds
503 float flipx = (float) ( iSprite->rank & 1 );
504 float flipy = (float) ( (iSprite->rank >> 1) & 1 );
505 // cu texture have a flat bottom so we can't do a vertical flip
506 if( iSprite->sprite_type == CLbox_cumulus )
508 // if( iSprite->sprite_type == CLbox_stratus )
510 // adjust colors depending on cloud type
511 // TODO : rewrite that later, still experimental
512 switch(iSprite->sprite_type) {
515 sgScaleVec3(iSprite->l0, 0.8f * dark);
516 sgScaleVec3(iSprite->l1, 0.8f * dark);
517 sgScaleVec3(iSprite->l2, dark);
518 sgScaleVec3(iSprite->l3, dark);
522 if( familly == CLFamilly_st ) {
523 sgScaleVec3(iSprite->l0, 0.8f);
524 sgScaleVec3(iSprite->l1, 0.8f);
525 sgScaleVec3(iSprite->l2, 0.8f);
526 sgScaleVec3(iSprite->l3, 0.8f);
528 sgScaleVec3(iSprite->l0, 0.7f);
529 sgScaleVec3(iSprite->l1, 0.7f);
530 sgScaleVec3(iSprite->l2, 0.7f);
531 sgScaleVec3(iSprite->l3, 0.7f);
535 // darker bottom than top
536 sgScaleVec3(iSprite->l0, 0.8f);
537 sgScaleVec3(iSprite->l1, 0.8f);
540 float r = iSprite->r * 0.5f;
542 sgVec4 l0, l1, l2, l3;
543 sgCopyVec4 ( l0, iSprite->l0 );
544 sgCopyVec4 ( l1, iSprite->l1 );
545 sgCopyVec4 ( l2, iSprite->l2 );
546 sgCopyVec4 ( l3, iSprite->l3 );
548 // now clouds at the far plane are half blended
549 sgScaleVec4( l0, t );
550 sgScaleVec4( l1, t );
551 sgScaleVec4( l2, t );
552 sgScaleVec4( l3, t );
554 if( iSprite->rank == clrank ) {
555 sgScaleVec4( l0, clfadeinrank );
556 sgScaleVec4( l1, clfadeinrank );
557 sgScaleVec4( l2, clfadeinrank );
558 sgScaleVec4( l3, clfadeinrank );
560 // compute the rotations so that the quad is facing the camera
562 sgSetVec3( pos, translate[SG_X], translate[SG_Z], translate[SG_Y] );
563 sgCopyVec3( translate, pos );
564 translate[2] -= FakeEyePos[1];
565 // sgNormaliseVec3( translate );
566 float dist_sprite = sgLengthVec3 ( translate );
567 sgScaleVec3 ( translate, SG_ONE / dist_sprite ) ;
568 sgVec3 x, y, up = {0.0f, 0.0f, 1.0f};
569 if( dist_sprite > 2*r ) {
570 sgVectorProductVec3(x, translate, up);
571 sgVectorProductVec3(y, x, translate);
573 sgCopyVec3( x, SGCloudField::view_X );
574 sgCopyVec3( y, SGCloudField::view_Y );
581 sgSetVec3( left, iSprite->pos[SG_X], iSprite->pos[SG_Z], iSprite->pos[SG_Y]);
583 sgCopyVec3( left, pos );
585 sgAddVec3 (right, left, x);
590 glTexCoord2f(flipx, 1.0f - flipy);
593 glTexCoord2f(1.0f - flipx, 1.0f - flipy);
595 sgScaleVec3( y, 2.0 );
597 sgAddVec3( right, y);
599 glTexCoord2f(1.0f - flipx, flipy);
602 glTexCoord2f(flipx, flipy);
611 // compute rotations so that a quad is facing the camera
612 // TODO:change obsolete code because we dont use glrotate anymore
613 void SGNewCloud::CalcAngles(sgVec3 refpos, sgVec3 FakeEyePos, float *angleY, float *angleX) {
614 sgVec3 upAux, lookAt, objToCamProj, objToCam;
617 sgSetVec3(objToCamProj, -FakeEyePos[SG_X] + refpos[SG_X], -FakeEyePos[SG_Z] + refpos[SG_Z], 0.0f);
618 sgNormaliseVec3(objToCamProj);
620 sgSetVec3(lookAt, 0.0f, 1.0f, 0.0f);
621 sgVectorProductVec3(upAux, lookAt, objToCamProj);
622 angle = sgScalarProductVec3(lookAt, objToCamProj);
623 if( (angle < 0.9999f) && (angle > -0.9999f) ) {
624 angle = acos(angle) * 180.0f / SG_PI;
625 if( upAux[2] < 0.0f )
630 sgSetVec3(objToCam, -FakeEyePos[SG_X] + refpos[SG_X], -FakeEyePos[SG_Z] + refpos[SG_Z], -FakeEyePos[SG_Y] + refpos[SG_Y]);
631 sgNormaliseVec3(objToCam);
633 angle2 = sgScalarProductVec3(objToCamProj, objToCam);
634 if( (angle2 < 0.9999f) && (angle2 > -0.9999f) ) {
635 angle2 = -acos(angle2) * 180.0f / SG_PI;
636 if( objToCam[2] > 0.0f )
647 // draw a cloud but this time we use the impostor texture
648 void SGNewCloud::RenderBB(sgVec3 deltaPos, bool first_time, float dist_center) {
651 sgSubVec3( translate, center, deltaPos);
653 // blend clouds with sky based on distance to limit the contrast of distant cloud
654 float CloudVisFade = (1.0f * SGCloudField::get_CloudVis());
656 float t = 1.0f - (dist_center - 1.0*radius) / CloudVisFade;
663 glColor4f(t, t, t, t);
665 // compute the rotations so that the quad is facing the camera
667 sgSetVec3( pos, translate[SG_X], translate[SG_Z], translate[SG_Y] );
668 sgCopyVec3( translate, pos );
669 pos[2] += deltaPos[1];
671 sgNormaliseVec3( translate );
672 sgVec3 x, y, up = {0.0f, 0.0f, 1.0f};
673 sgVectorProductVec3(x, translate, up);
674 sgVectorProductVec3(y, x, translate);
676 sgCopyVec3( rotX, x );
677 sgCopyVec3( rotY, y );
678 } else if(fabs(sgScalarProductVec3(rotX, x)) < 0.93f || fabs(sgScalarProductVec3(rotY, y)) < 0.93f ) {
679 // ask for a redraw of this impostor if the view angle changed too much
680 sgCopyVec3( rotX, x );
681 sgCopyVec3( rotY, y );
682 cldCache->invalidate(cloudId, bbId);
688 sgCopyVec3( left, pos );
690 sgAddVec3 (right, left, x);
694 glTexCoord2f(0.0f, 0.0f);
696 glTexCoord2f(1.0f, 0.0f);
698 sgScaleVec3( y, 2.0 );
700 sgAddVec3( right, y);
701 glTexCoord2f(1.0f, 1.0f);
703 glTexCoord2f(0.0f, 1.0f);
708 int age = cldCache->queryImpostorAge(bbId);
709 // draw a red border for the newly generated BBs else draw a white border
715 glBindTexture(GL_TEXTURE_2D, 0);
716 glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
723 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
729 // determine if it is a good idea to use an impostor to render the cloud
730 bool SGNewCloud::isBillboardable(float dist) {
732 if( dist <= ( 2.1f * radius ) ) {
736 if( (dist-radius) <= nearRadius ) {
737 // near clouds we don't want to use BB
745 // render the cloud, fakepos is a relative position inside the cloud field
746 void SGNewCloud::Render(sgVec3 FakeEyePos) {
750 sgCopyVec3( deltaPos, FakeEyePos);
752 sgSubVec3( dist, center, FakeEyePos);
753 float dist_center = sgLengthVec3(dist);
756 fadetimer += SGCloudField::timer_dt;
757 if( fadetimer > duration + pauseLength ) {
758 // fade out after fade in, and vice versa
759 direction = ! direction;
764 if( !isBillboardable(dist_center) ) {
765 // not a good candidate for impostors, draw a real cloud
766 Render3Dcloud(false, FakeEyePos, deltaPos, dist_center);
769 bool first_time = false;
770 // lets use our impostor
772 texID = cldCache->QueryTexID(cloudId, bbId);
774 // ok someone took our impostor, so allocate a new one
776 // allocate a new Impostor
777 bbId = cldCache->alloc(cloudId);
778 texID = cldCache->QueryTexID(cloudId, bbId);
782 // no more free texture in the pool
783 Render3Dcloud(false, FakeEyePos, deltaPos, dist_center);
785 float angleX=0.0f, angleY=0.0f;
787 // force a redraw of the impostor if the cloud shape has changed enought
788 float step = ( list_spriteDef.size() * (direction ? fadetimer : duration-fadetimer)) / duration;
789 if( fabs(step - last_step) > 0.5f )
790 cldCache->invalidate(cloudId, bbId);
792 if( ! cldCache->isBbValid( cloudId, bbId, angleY, angleX)) {
793 // we must build or rebuild this billboard
794 // start render to texture
795 cldCache->beginCapture();
796 // set transformation matrices
797 cldCache->setRadius(radius, dist_center);
798 gluLookAt(FakeEyePos[SG_X], FakeEyePos[SG_Z], FakeEyePos[SG_Y], center[SG_X], center[SG_Z], center[SG_Y], 0.0, 0.0, 1.0);
800 Render3Dcloud(true, FakeEyePos, deltaPos, dist_center);
801 // save rotation angles for later use
802 // TODO:this is not ok
803 cldCache->setReference(cloudId, bbId, angleY, angleX);
804 // save the rendered cloud into the cache
805 cldCache->setTextureData( bbId );
806 // finish render to texture and go back into standard context
807 cldCache->endCapture();
809 // draw the newly built BB or an old one
810 glBindTexture(GL_TEXTURE_2D, texID);
811 RenderBB(FakeEyePos, first_time, dist_center);