]> git.mxchange.org Git - simgear.git/commitdiff
Harald Johnson:
authorehofman <ehofman>
Sun, 24 Apr 2005 11:16:50 +0000 (11:16 +0000)
committerehofman <ehofman>
Sun, 24 Apr 2005 11:16:50 +0000 (11:16 +0000)
- new and updated sources for the new volumetric clouds
- 2 new textures for the clouds
- an update to the render dialog to enable/disable and change a few parameters
  for the new clouds

14 files changed:
simgear/environment/Makefile.am
simgear/environment/visual_enviro.cxx [new file with mode: 0644]
simgear/environment/visual_enviro.cxx~ [new file with mode: 0644]
simgear/environment/visual_enviro.hxx [new file with mode: 0644]
simgear/environment/visual_enviro.hxx~ [new file with mode: 0644]
simgear/scene/sky/Makefile.am
simgear/scene/sky/bbcache.cxx [new file with mode: 0644]
simgear/scene/sky/bbcache.hxx [new file with mode: 0644]
simgear/scene/sky/cloud.cxx
simgear/scene/sky/cloud.hxx
simgear/scene/sky/cloudfield.cxx [new file with mode: 0644]
simgear/scene/sky/cloudfield.hxx [new file with mode: 0644]
simgear/scene/sky/newcloud.cxx [new file with mode: 0644]
simgear/scene/sky/newcloud.hxx [new file with mode: 0644]

index 1948696e466aeb9c891a48e10c64f1901a985891..5505aa5dd169b2b03e5bfb89162a271a476ee6be 100644 (file)
@@ -2,8 +2,8 @@ includedir = @includedir@/environment
 
 lib_LIBRARIES = libsgenvironment.a
 
-include_HEADERS = metar.hxx
+include_HEADERS = metar.hxx visual_enviro.hxx
 
-libsgenvironment_a_SOURCES = metar.cxx
+libsgenvironment_a_SOURCES = metar.cxx visual_enviro.cxx
 
 INCLUDES = -I$(top_srcdir)
diff --git a/simgear/environment/visual_enviro.cxx b/simgear/environment/visual_enviro.cxx
new file mode 100644 (file)
index 0000000..0fdeae2
--- /dev/null
@@ -0,0 +1,81 @@
+// Visual environment helper class
+//
+// Written by Harald JOHNSEN, started April 2005.
+//
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+//
+//
+
+#include <simgear/scene/sky/cloudfield.hxx>
+#include <simgear/scene/sky/newcloud.hxx>
+#include "visual_enviro.hxx"
+
+SGEnviro sgEnviro;
+
+SGEnviro::SGEnviro(void) {
+}
+
+SGEnviro::~SGEnviro(void) {
+}
+
+int SGEnviro::get_clouds_CacheSize(void) const {
+       return SGCloudField::get_CacheSize();
+}
+float SGEnviro::get_clouds_visibility(void) const {
+       return SGCloudField::get_CloudVis();
+}
+float SGEnviro::get_clouds_density(void) const {
+       return SGCloudField::get_density();
+}
+bool SGEnviro::get_clouds_enable_state(void) const {
+       return SGCloudField::get_enable3dClouds();
+}
+
+void SGEnviro::set_clouds_CacheSize(int sizeKb) {
+       SGCloudField::set_CacheSize(sizeKb);
+}
+void SGEnviro::set_clouds_visibility(float distance) {
+       SGCloudField::set_CloudVis(distance);
+}
+void SGEnviro::set_clouds_density(float density) {
+       SGCloudField::set_density(density);
+}
+void SGEnviro::set_clouds_enable_state(bool enable) {
+       SGCloudField::set_enable3dClouds(enable);
+}
+
+// rain/snow
+float SGEnviro::get_precipitation_density(void) const {
+       return 0.0;
+}
+bool SGEnviro::get_precipitation_enable_state(void) const {
+       return false;
+}
+
+void SGEnviro::set_precipitation_density(float density) {
+}
+void SGEnviro::set_precipitation_enable_state(bool enable) {
+}
+
+// others
+bool SGEnviro::get_lightning_enable_state(void) const {
+       return false;
+}
+
+void SGEnviro::set_lightning_enable_state(bool enable) {
+}
+
diff --git a/simgear/environment/visual_enviro.cxx~ b/simgear/environment/visual_enviro.cxx~
new file mode 100644 (file)
index 0000000..7af67e0
--- /dev/null
@@ -0,0 +1,81 @@
+// Visual environment helper class\r
+//\r
+// Written by Harald JOHNSEN, started April 2005.\r
+//\r
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License as\r
+// published by the Free Software Foundation; either version 2 of the\r
+// License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful, but\r
+// WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+// General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA\r
+//\r
+//\r
+\r
+#include <simgear/scene/sky/cloudfield.hxx>\r
+#include <simgear/scene/sky/newcloud.hxx>\r
+#include "visual_enviro.hxx"\r
+\r
+SGEnviro sgEnviro;\r
+\r
+SGEnviro::SGEnviro(void) {\r
+}\r
+\r
+SGEnviro::~SGEnviro(void) {\r
+}\r
+\r
+int SGEnviro::get_clouds_CacheSize(void) const {\r
+       return SGCloudField::get_CacheSize();\r
+}\r
+float SGEnviro::get_clouds_visibility(void) const {\r
+       return SGCloudField::get_CloudVis();\r
+}\r
+float SGEnviro::get_clouds_density(void) const {\r
+       return SGCloudField::get_density();\r
+}\r
+bool SGEnviro::get_clouds_enable_state(void) const {\r
+       return SGCloudField::get_enable3dClouds();\r
+}\r
+\r
+void SGEnviro::set_clouds_CacheSize(int sizeKb) {\r
+       SGCloudField::set_CacheSize(sizeKb);\r
+}\r
+void SGEnviro::set_clouds_visibility(float distance) {\r
+       SGCloudField::set_CloudVis(distance);\r
+}\r
+void SGEnviro::set_clouds_density(float density) {\r
+       SGCloudField::set_density(density);\r
+}\r
+void SGEnviro::set_clouds_enable_state(bool enable) {\r
+       SGCloudField::set_enable3dClouds(enable);\r
+}\r
+\r
+// rain/snow\r
+float SGEnviro::get_precipitation_density(void) const {\r
+       return 0.0;\r
+}\r
+bool SGEnviro::get_precipitation_enable_state(void) const {\r
+       return false;\r
+}\r
+\r
+void SGEnviro::set_precipitation_density(float density) {\r
+}\r
+void SGEnviro::set_precipitation_enable_state(bool enable) {\r
+}\r
+\r
+// others\r
+bool SGEnviro::get_lightning_enable_state(void) const {\r
+       return false;\r
+}\r
+\r
+void SGEnviro::set_lightning_enable_state(bool enable) {\r
+}\r
+\r
diff --git a/simgear/environment/visual_enviro.hxx b/simgear/environment/visual_enviro.hxx
new file mode 100644 (file)
index 0000000..cc2a65d
--- /dev/null
@@ -0,0 +1,58 @@
+// Visual environment helper class
+//
+// Written by Harald JOHNSEN, started April 2005.
+//
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+//
+//
+#ifndef _VISUAL_ENVIRO_HXX
+#define _VISUAL_ENVIRO_HXX
+
+class SGEnviro {
+
+public:
+       SGEnviro();
+       ~SGEnviro();
+
+       // Clouds
+       // return the size of the memory pool used by texture impostors
+       int get_clouds_CacheSize(void) const;
+       float get_clouds_visibility(void) const;
+       float get_clouds_density(void) const;
+       bool get_clouds_enable_state(void) const;
+
+       void set_clouds_CacheSize(int sizeKb);
+       void set_clouds_visibility(float distance);
+       void set_clouds_density(float density);
+       void set_clouds_enable_state(bool enable);
+
+       // rain/snow
+       float get_precipitation_density(void) const;
+       bool get_precipitation_enable_state(void) const;
+
+       void set_precipitation_density(float density);
+       void set_precipitation_enable_state(bool enable);
+
+       // others
+       bool get_lightning_enable_state(void) const;
+       void set_lightning_enable_state(bool enable);
+};
+
+extern SGEnviro sgEnviro;
+
+#endif // _VISUAL_ENVIRO_HXX
+
diff --git a/simgear/environment/visual_enviro.hxx~ b/simgear/environment/visual_enviro.hxx~
new file mode 100644 (file)
index 0000000..1af1dce
--- /dev/null
@@ -0,0 +1,58 @@
+// Visual environment helper class\r
+//\r
+// Written by Harald JOHNSEN, started April 2005.\r
+//\r
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net\r
+//\r
+// This program is free software; you can redistribute it and/or\r
+// modify it under the terms of the GNU General Public License as\r
+// published by the Free Software Foundation; either version 2 of the\r
+// License, or (at your option) any later version.\r
+//\r
+// This program is distributed in the hope that it will be useful, but\r
+// WITHOUT ANY WARRANTY; without even the implied warranty of\r
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
+// General Public License for more details.\r
+//\r
+// You should have received a copy of the GNU General Public License\r
+// along with this program; if not, write to the Free Software\r
+// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA\r
+//\r
+//\r
+#ifndef _VISUAL_ENVIRO_HXX\r
+#define _VISUAL_ENVIRO_HXX\r
+\r
+class SGEnviro {\r
+\r
+public:\r
+       SGEnviro();\r
+       ~SGEnviro();\r
+\r
+       // Clouds\r
+       // return the size of the memory pool used by texture impostors\r
+       int get_clouds_CacheSize(void) const;\r
+       float get_clouds_visibility(void) const;\r
+       float get_clouds_density(void) const;\r
+       bool get_clouds_enable_state(void) const;\r
+\r
+       void set_clouds_CacheSize(int sizeKb);\r
+       void set_clouds_visibility(float distance);\r
+       void set_clouds_density(float density);\r
+       void set_clouds_enable_state(bool enable);\r
+\r
+       // rain/snow\r
+       float get_precipitation_density(void) const;\r
+       bool get_precipitation_enable_state(void) const;\r
+\r
+       void set_precipitation_density(float density);\r
+       void set_precipitation_enable_state(bool enable);\r
+\r
+       // others\r
+       bool get_lightning_enable_state(void) const;\r
+       void set_lightning_enable_state(bool enable);\r
+};\r
+\r
+extern SGEnviro sgEnviro;\r
+\r
+#endif // _VISUAL_ENVIRO_HXX
+
index efc38b9ae939ffec155495692ddf3c0852034470..177633dc06b20aa376dfe264a58696e6265e7a0c 100644 (file)
@@ -11,7 +11,10 @@ include_HEADERS = \
        oursun.hxx \
        sky.hxx \
        sphere.hxx \
-       stars.hxx
+       stars.hxx \
+       bbcache.hxx \
+       cloudfield.hxx \
+       newcloud.hxx
 
 libsgsky_a_SOURCES = \
        cloud.cxx \
@@ -20,6 +23,9 @@ libsgsky_a_SOURCES = \
        oursun.cxx \
        sky.cxx \
        sphere.cxx \
-       stars.cxx
+       stars.cxx \
+       bbcache.cxx \
+       cloudfield.cxx \
+       newcloud.cxx
 
 INCLUDES = -I$(top_srcdir)
diff --git a/simgear/scene/sky/bbcache.cxx b/simgear/scene/sky/bbcache.cxx
new file mode 100644 (file)
index 0000000..ad253cd
--- /dev/null
@@ -0,0 +1,339 @@
+// Billboard helper class
+//
+// Written by Harald JOHNSEN, started April 2005.
+//
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+//
+//
+
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
+#include <simgear/compiler.h>
+
+#include <plib/sg.h>
+#include <plib/ssg.h>
+#include <simgear/screen/extensions.hxx>
+#include <simgear/screen/RenderTexture.h>
+#include SG_GLUT_H
+
+#include "bbcache.hxx"
+
+
+/*
+memory usage :
+size          1 tex       8 tex       32 tex      64 tex
+64x64x4        16k        128k        512k         1Mo
+128x128x4      64k        512k        2Mo          4Mo
+256x256x4     256k        2Mo         8Mo         16Mo
+*/
+
+void SGBbCache::freeTextureMemory(void) {
+
+       if( bbListCount ) {
+               for(int i = 0 ; i < bbListCount ; i++) {
+                       if(bbList[i].texID)
+                               glDeleteTextures(1, & bbList[i].texID);
+               }
+               delete [] bbList;
+       }
+       bbListCount = 0;
+       cacheSizeKb = 0;
+       textureWH   = 0;
+}
+
+bool SGBbCache::allocTextureMemory(int cacheCount, int textureDimension) {
+       textureWH = textureDimension;
+       bbListCount = cacheCount;
+       bbList = new bbInfo[bbListCount];
+       for(int i = 0 ; i < bbListCount ; i++) {
+               bbList[i].cldID = 0;
+               bbList[i].texID = 0;
+        glGenTextures(1, &bbList[i].texID);
+        glBindTexture(GL_TEXTURE_2D, bbList[i].texID);
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 
+                         textureDimension, textureDimension, 0, GL_RGB, GL_FLOAT, NULL);
+
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+       }
+    glBindTexture(GL_TEXTURE_2D, 0);
+       cacheSizeKb = (textureDimension * textureDimension * 4);
+       cacheSizeKb *= cacheCount;
+       cacheSizeKb /= 1024;
+       if(rt) {
+               rt->BeginCapture();
+               glViewport(0, 0, textureDimension, textureDimension);
+               rt->EndCapture();
+       }
+       return true;
+}
+
+SGBbCache::SGBbCache(void) :
+       bbListCount(0),
+       cacheSizeKb(0),
+       textureWH(0),
+       builtBBCount(0),
+       rt(0),
+       rtAvailable(false),
+       frameNumber(0),
+       maxImpostorRegenFrame(10)
+{
+}
+
+SGBbCache::~SGBbCache(void) {
+       if(rt)
+               delete rt;
+       freeTextureMemory();
+}
+
+
+void SGBbCache::init(int cacheCount) {
+
+       rt = new RenderTexture();
+       // don't use default rtt on nvidia/win because of poor performance of glCopyTexSubImage2D
+       // wihtout default pattrib params - see opengl forum
+       rt->Reset("rgba tex2D ctt");
+//     rt->Reset("rgba tex2D");
+       if( rt->Initialize(256, 256, true) ) {
+               rtAvailable = true;
+               if (rt->BeginCapture())
+               {
+                       glViewport(0, 0, 256, 256);
+                       glMatrixMode(GL_PROJECTION);
+                       glLoadIdentity();
+                       gluPerspective(60.0,  1, 1, 5.0);
+                       glMatrixMode(GL_MODELVIEW);
+                       glLoadIdentity();
+                       glDisable(GL_LIGHTING);
+                       glEnable(GL_COLOR_MATERIAL);
+                       glDisable(GL_CULL_FACE);
+                       glDisable(GL_FOG);
+                       glDisable(GL_DEPTH_TEST);
+                       glClearColor(0.0, 0.0, 0.0, 0.0);
+                       glEnable(GL_TEXTURE_2D);
+                       glEnable(GL_ALPHA_TEST);
+                       glAlphaFunc(GL_GREATER, 0.0f);
+                       glEnable(GL_SMOOTH);
+                       glEnable(GL_BLEND);
+                       glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
+
+                       rt->EndCapture();
+               }
+       }
+       if( cacheCount )
+               allocTextureMemory( cacheCount, 256 );
+
+}
+
+// TODO:not callable atm, texture size not handled correctly
+bool SGBbCache::setCacheSize(int count, int textureDimension) {
+       if( count < 0 || count > 500)
+               return false;
+       freeTextureMemory();
+       if( count == 0)
+               return true;
+
+       // only allow some reasonable dimensions
+       switch(textureDimension) {
+               case 0:
+                       // default size
+                       textureDimension = 256;
+                       break;
+               case 64:
+               case 128:
+               case 256:
+                       break;
+               case 512:
+                       // rt is 256 so higher texture size has no meaning
+                       textureDimension = 256;
+                       break;
+               default:
+                       textureDimension = 128;
+                       break;
+       }
+       return allocTextureMemory( count, textureDimension);
+}
+
+// TODO:not callable atm, texture size not handled correctly
+bool SGBbCache::setCacheSize(int sizeKb) {
+       if( sizeKb < 0 || sizeKb > 256*1024)
+               return false;
+       freeTextureMemory();
+       if( sizeKb == 0)
+               return true;
+       int count = 1;
+       int textureDimension = 256;
+       if( cacheSizeKb >= 8*1024 ) {
+               // more than 32 256x256 textures
+               textureDimension = 256;
+       } else  if( cacheSizeKb >= 2*1024 ) {
+               // more than 32 128x128 textures
+               textureDimension = 128;
+       } else  {
+               // don't go under 64x64 textures
+               textureDimension = 64;
+       }
+       count = (sizeKb * 1024) / (textureDimension * textureDimension * 4);
+       if(count == 0)
+               count = 1;
+       return allocTextureMemory( count, textureDimension);
+}
+
+int SGBbCache::queryCacheSize(void) {
+       return cacheSizeKb;
+}
+
+void SGBbCache::free(int bbId, int cldId) {
+       if( bbId < 0 || bbId >= bbListCount )
+               return;
+       if( bbList[bbId].cldID != cldId )
+               return;
+       bbList[bbId].cldID = 0;
+}
+
+int SGBbCache::alloc(int cldId) {
+       // pretend we have no more texture if render to texture is not available
+       if( ! rtAvailable )
+               return -1;
+       for(int i = 0 ; i < bbListCount ; i++) {
+               if( (bbList[i].cldID == 0) && (bbList[i].texID != 0) ) {
+            bbList[i].cldID = cldId;
+                       bbList[i].angleX = -999;
+                       bbList[i].angleY = -999;
+                       bbList[i].frameUsed = 0;
+                       return i;
+               }
+       }
+       return -1;
+}
+
+GLuint SGBbCache::QueryTexID(int cldId, int bbId) {
+       if( bbId < 0 || bbId >= bbListCount )
+               return 0;
+       if( bbList[bbId].cldID != cldId )
+               return 0;
+       return bbList[bbId].texID;
+}
+
+int SGBbCache::queryImpostorAge(int bbId) {
+       if( bbId < 0 || bbId >= bbListCount )
+               return 0;
+       return frameNumber - bbList[bbId].frame;
+}
+
+void SGBbCache::beginCapture(void) {
+
+       rt->BeginCapture();
+
+       glClear(GL_COLOR_BUFFER_BIT);
+
+}
+
+
+
+void SGBbCache::setRadius(float radius, float dist_center) {
+       float border;
+       //set viewport to texture resolution
+       //glViewport(0, 0, 256, 256);
+       glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+
+    float near_ = dist_center - radius;
+    float far_ = dist_center + radius;
+       if( near_ <= 0 ) {
+        // we are in trouble
+        glFrustum(-1, 1, -1, 1, 1, 1 + radius * 2);
+       } else {
+        border = (near_ * radius) / sqrt(dist_center * dist_center - radius * radius);
+        glFrustum(-border, border, -border, border, near_, far_);
+       }
+    glMatrixMode(GL_MODELVIEW);
+    glLoadIdentity();
+}
+void SGBbCache::setTextureData(int bbId) {
+       if( bbId < 0 || bbId >= bbListCount )
+               return;
+
+    glBindTexture(GL_TEXTURE_2D, bbList[bbId].texID);
+       glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, textureWH, textureWH);
+//    bbList[bbId].angleX = angleX;
+//    bbList[bbId].angleY = angleY;
+    bbList[bbId].frame = frameNumber;
+       bbList[bbId].frameUsed = frameNumber;
+    builtBBCount ++;
+       builtBBframe ++;
+}
+
+void SGBbCache::endCapture(void) {
+
+       rt->EndCapture();
+//    glBindTexture(GL_TEXTURE_2D, rt->GetTextureID() );
+
+}
+
+
+bool SGBbCache::isBbValid( int cldId, int bbId, float angleY, float angleX) {
+       if( bbId < 0 || bbId >= bbListCount )
+               return false;
+       if( bbList[bbId].cldID != cldId )
+               return false;
+
+       // it was just allocated
+       if( bbList[bbId].frameUsed == 0)
+               return false;
+
+       // we reuse old impostor to speed up things
+       if( builtBBframe >= maxImpostorRegenFrame )
+               return true;
+
+    if( fabs(angleY - bbList[bbId].angleY) >= 5.0 )
+        return false;
+
+    if( fabs(angleX - bbList[bbId].angleX) >= 5.0 )
+        return false;
+
+       bbList[bbId].frameUsed = frameNumber;
+       return true;
+}
+
+// TODO:this is not the right way to handle that
+void SGBbCache::setReference( int cldId, int bbId, float angleY, float angleX) {
+       if( bbId < 0 || bbId >= bbListCount )
+               return;
+       if( bbList[bbId].cldID != cldId )
+               return;
+       bbList[bbId].angleX = angleX;
+       bbList[bbId].angleY = angleY;
+}
+
+void SGBbCache::startNewFrame(void) {
+       builtBBframe = 0;
+       // TOTO:find reasonable value
+       int minFrameNumber = frameNumber - 500;
+       frameNumber++;
+       // cleanup of unused enties
+       for( int bbId = 0 ; bbId < bbListCount ; bbId++)
+               if( (bbList[bbId].cldID != 0) && (bbList[bbId].frameUsed < minFrameNumber) ) {
+                       // entry is now free
+                       bbList[bbId].cldID = 0;
+               }
+}
diff --git a/simgear/scene/sky/bbcache.hxx b/simgear/scene/sky/bbcache.hxx
new file mode 100644 (file)
index 0000000..f2ba564
--- /dev/null
@@ -0,0 +1,117 @@
+// Billboard helper class
+//
+// Written by Harald JOHNSEN, started April 2005.
+//
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+//
+//
+
+#ifndef _BBCACHE_HXX
+#define _BBCACHE_HXX
+
+
+#include <plib/sg.h>
+#include <plib/ssg.h>
+#include <simgear/screen/extensions.hxx>
+#include <simgear/screen/RenderTexture.h>
+
+class SGBbCache {
+private:
+
+       typedef struct {
+               GLuint  texID;
+               int             cldID;
+               float   angleX, angleY;
+               // creation frame number for debug only
+               int             frame;
+               // last time this entry was used
+               int             frameUsed;
+       } bbInfo;
+
+       void freeTextureMemory(void);
+       bool allocTextureMemory(int count, int textureDimension);
+
+       // a list of impostors
+       bbInfo  *bbList;
+       int             bbListCount;
+       int             textureWH;
+       int             cacheSizeKb;
+
+       // for debug only, stats
+       int             builtBBCount;
+       // count of generated BB during the current frame
+       int             builtBBframe;
+
+       long    frameNumber;
+       RenderTexture *rt;
+       bool    rtAvailable;
+
+public:
+       SGBbCache(void);
+       ~SGBbCache(void);
+
+       // call this first to initialize everything, cacheCount is the number of texture to allocate
+       void init(int cacheCount);
+
+       // free one cache slot, usualy when the cached object is destroyed
+       void free(int bbId, int cldId);
+
+       // allocate a new texture, return an index in the cache
+       int alloc(int cldId);
+
+       // give the texture name to use
+       GLuint QueryTexID(int cldId, int bbId);
+
+       // save the rendered texture from the current context to a new texture
+       void setTextureData(int bbId);
+
+       // start the rendering of a billboard in the RTT context
+       void beginCapture(void);
+
+       // adjust the projection matrix of the RTT context to the size of the object
+       void setRadius(float radius, float dist_center);
+
+       // forget the RTT and go back to the previous rendering context
+       void endCapture(void);
+
+       // for debugging only, give the number of frames since the inpostor was built
+       int queryImpostorAge(int bbId);
+
+       // can we still use this impostor ?
+       bool isBbValid( int cloudId, int bbId, float angleY, float angleX);
+
+       // save view angles of this billboard
+       void setReference( int cloudId, int bbId, float angleY, float angleX);
+
+       // prepare the cache for the rendering of a new frame
+       void startNewFrame(void);
+
+       // alloc the impostors texture memory given the size of the memory pool
+       // if sizeKb == 0 then the memory pool is freed and impostors are disabled
+       bool setCacheSize(int sizeKb);
+
+       // alloc the impostors texture memory given the count and size of texture
+       // if count == 0 then the memory pool is freed and impostors are disabled
+       bool setCacheSize(int count, int textureDimension);
+
+       // return the size of the memory pool used by texture impostors
+       int queryCacheSize(void);
+
+       int maxImpostorRegenFrame;
+};
+
+#endif // _BBCACHE_HXX
index 24b03e967fa13e0849d7474c9a622784987fe586..776044a3c5cf13624ebfb66d4157813c362098c7 100644 (file)
@@ -39,6 +39,8 @@ inline int (isnan)(double r) { return isnan(r); }
 #include <simgear/screen/extensions.hxx>
 #include <simgear/screen/texture.hxx>
 
+#include "newcloud.hxx"
+#include "cloudfield.hxx"
 #include "cloud.hxx"
 
 #if defined(__MINGW32__)
@@ -221,12 +223,14 @@ SGCloudLayer::SGCloudLayer( const string &tex_path ) :
     layer[0] = layer[1] = layer[2] = layer[3] = NULL;
 
     layer_root->addKid(layer_transform);
+       layer3D = new SGCloudField;
     rebuild();
 }
 
 // Destructor
 SGCloudLayer::~SGCloudLayer()
 {
+       delete layer3D;
     delete vertices;
     delete indices;
     delete layer_root;         // deletes layer_transform and layer as well
@@ -460,6 +464,8 @@ SGCloudLayer::rebuild()
 
             layer_states[SG_CLOUD_CLEAR] = 0;
         }
+               SGNewCloud::loadTextures(texture_path.str());
+               layer3D->buildTestLayer();
     }
 
     if ( bump_mapping ) {
@@ -837,6 +843,7 @@ bool SGCloudLayer::reposition( sgVec3 p, sgVec3 up, double lon, double lat,
         last_lat = lat;
     }
 
+       layer3D->reposition( p, up, lon, lat, alt, dt);
     return true;
 }
 
@@ -844,6 +851,9 @@ bool SGCloudLayer::reposition( sgVec3 p, sgVec3 up, double lon, double lat,
 void SGCloudLayer::draw( bool top ) {
     if ( layer_coverage != SG_CLOUD_CLEAR ) {
 
+               if ( SGCloudField::enable3D )
+                       layer3D->Render();
+               else
         if ( bump_mapping && enable_bump_mapping ) {
 
             sgMat4 modelview,
index dbd7c8fb1cdfe154584d00fdcfd53a9a8f76ebf9..9d0f139e6a3c3d113cdf8849ab1694c23fd0f5e5 100644 (file)
@@ -39,6 +39,7 @@ SG_USING_STD(string);
 // SG_USING_STD(cout);
 // SG_USING_STD(endl);
 
+class SGCloudField;
 
 /**
  * A class layer to model a single cloud layer
@@ -225,6 +226,8 @@ private:
     // position, not view position
     // double xoff, yoff;
     double last_lon, last_lat, last_course;
+
+       SGCloudField *layer3D;
 };
 
 
diff --git a/simgear/scene/sky/cloudfield.cxx b/simgear/scene/sky/cloudfield.cxx
new file mode 100644 (file)
index 0000000..a847c57
--- /dev/null
@@ -0,0 +1,327 @@
+// a layer of 3d clouds
+//
+// Written by Harald JOHNSEN, started April 2005.
+//
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+//
+//
+
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
+#include <simgear/compiler.h>
+
+#include <plib/sg.h>
+#include <plib/ssg.h>
+#include <simgear/math/sg_geodesy.hxx>
+
+#include STL_ALGORITHM
+#include SG_GLUT_H
+#include <vector>
+
+SG_USING_STD(vector);
+
+#include "newcloud.hxx"
+#include "cloudfield.hxx"
+
+static list_of_culledCloud inViewClouds;
+
+// visibility distance for clouds in meters
+float SGCloudField::CloudVis = 25000.0f;
+bool SGCloudField::enable3D = true;
+// fieldSize must be > CloudVis or we can destroy the impostor cache
+// a cloud must only be seen once or the impostor will be generated for each of his positions
+double SGCloudField::fieldSize = 30000.0;
+float SGCloudField::density = 100.0;
+static int last_cache_size = 4*1024;
+
+int SGCloudField::get_CacheSize(void) { 
+       return SGNewCloud::cldCache->queryCacheSize(); 
+}
+
+void SGCloudField::set_CacheSize(int sizeKb) {
+       // apply in rendering option dialog
+       if(last_cache_size == sizeKb)
+               return;
+       if(sizeKb == 0)
+               return;
+       if(sizeKb)
+               last_cache_size = sizeKb;
+       SGNewCloud::cldCache->setCacheSize(sizeKb);
+}
+void SGCloudField::set_CloudVis(float distance) {
+       SGCloudField::CloudVis = distance;
+}
+void SGCloudField::set_density(float density) {
+       SGCloudField::density = density;
+}
+void SGCloudField::set_enable3dClouds(bool enable) {
+       if(enable3D == enable)
+               return;
+       enable3D = enable;
+       if(enable) {
+               SGNewCloud::cldCache->setCacheSize(last_cache_size);
+       } else {
+               SGNewCloud::cldCache->setCacheSize(0);
+       }
+}
+
+// reposition the cloud layer at the specified origin and orientation
+void SGCloudField::reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt) {
+    sgMat4 T1, LON, LAT;
+    sgVec3 axis;
+
+    sgMakeTransMat4( T1, p );
+
+    sgSetVec3( axis, 0.0, 0.0, 1.0 );
+    sgMakeRotMat4( LON, lon * SGD_RADIANS_TO_DEGREES, axis );
+
+    sgSetVec3( axis, 0.0, 1.0, 0.0 );
+    sgMakeRotMat4( LAT, 90.0 - lat * SGD_RADIANS_TO_DEGREES, axis );
+
+    sgMat4 TRANSFORM;
+
+    sgCopyMat4( TRANSFORM, T1 );
+    sgPreMultMat4( TRANSFORM, LON );
+    sgPreMultMat4( TRANSFORM, LAT );
+
+    sgCoord layerpos;
+    sgSetCoord( &layerpos, TRANSFORM );
+
+       sgMakeCoordMat4( transform, &layerpos );
+
+       // TODO:use a simple sphere earth
+       double az1, az2, s;
+       geo_inverse_wgs_84( 0.0, 0.0, 0.0, lat*SG_RADIANS_TO_DEGREES , lon*SG_RADIANS_TO_DEGREES, &az1, &az2, &s);
+       az1 = az1 * SG_DEGREES_TO_RADIANS;
+       // compute the view position on a 'flat' earth
+       deltay = -cos(SG_PI/2+az1) * s;
+       deltax = -sin(SG_PI/2+az1) * s;
+//     deltax = cos(0.0) * s;
+//     deltay = sin(0.0) * s;
+       this->alt = alt;
+
+       // simulate clouds movement from wind
+       double speed = 10.0f;
+       double direction = 45.0;
+    double sp_dist = speed*dt;
+    if (sp_dist > 0) {
+        double bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
+        double by = sin((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
+               relative_position[SG_X] += bx;
+               relative_position[SG_Y] += by;
+    }
+
+       // correct the frustum with the right far plane
+       ssgContext *context = ssgGetCurrentContext();
+       frustum = *context->getFrustum();
+       frustum.setNearFar(1.0, CloudVis);
+}
+
+SGCloudField::SGCloudField() {
+       sgSetVec3( relative_position, 0,0,0);
+       theField.reserve(200);
+       inViewClouds.reserve(200);
+}
+
+SGCloudField::~SGCloudField() {
+       list_of_Cloud::iterator iCloud;
+       for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
+               delete iCloud->aCloud;
+       }
+       theField.clear();
+}
+
+
+// add one cloud, data is not copied, ownership given
+void SGCloudField::addCloud( sgVec3 pos, SGNewCloud *cloud) {
+       Cloud cl;
+       sgCopyVec3( cl.pos, pos );
+       cl.aCloud = cloud;
+       cloud->SetPos( pos );
+       theField.push_back( cl );
+}
+
+
+static float Rnd(float n) {
+       return n * (-0.5f + rand() / (float) RAND_MAX);
+}
+
+// for debug only
+// build a field of cloud of size 25x25 km, its a grid of 11x11 clouds
+void SGCloudField::buildTestLayer(void) {
+
+       const float s = 2200.0f;
+
+       for( int z = -5 ; z <= 5 ; z++) {
+               for( int x = -5 ; x <= 5 ; x++ ) {
+            SGNewCloud *cloud = new SGNewCloud;
+                       cloud->new_cu();
+                       sgVec3 pos = {(x+Rnd(0.7)) * s, 750.0f, (z+Rnd(0.7)) * s};
+            addCloud(pos, cloud);
+               }
+       }
+
+}
+
+// cull all clouds of a tiled field
+void SGCloudField::cullClouds(sgVec3 eyePos, sgMat4 mat) {
+       list_of_Cloud::iterator iCloud;
+//     const float distVisCompare = CloudVis * CloudVis;
+
+       // TODO:cull the field before culling the clouds in the field (should eliminate 3 fields)
+       for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
+               sgVec3 dist;
+               sgSphere sphere;
+               sgSubVec3( dist, iCloud->pos, eyePos );
+               sphere.setCenter(dist[0], dist[2], dist[1]);
+               sphere.setRadius(iCloud->aCloud->getRadius());
+               sphere.orthoXform(mat);
+               if( frustum.contains( & sphere ) != SG_OUTSIDE ) {
+                       float squareDist = dist[0]*dist[0] + dist[1]*dist[1] + dist[2]*dist[2];
+                       culledCloud tmp;
+                       tmp.aCloud = iCloud->aCloud;
+                       sgCopyVec3( tmp.eyePos, eyePos ); 
+                       // save distance for later sort, opposite distance because we want back to front
+                       tmp.dist   =  - squareDist;
+                       inViewClouds.push_back(tmp);
+               }
+       }
+
+}
+
+static inline void myswap(float &a, float &b) {
+       float tmp = a;
+       a = b;
+       b = tmp;
+}
+
+// Render a cloud field
+// because no field can have an infinite size (and we don't want to reach his border)
+// we draw this field and adjacent fields.
+// adjacent fields are not real, its the same field displaced by some offset
+void SGCloudField::Render(void) {
+    sgVec3 eyePos;
+       double relx, rely;
+
+       if( ! enable3D )
+               return;
+
+       // ask the impostor cache to do some cleanup
+       // TODO:don't do that for every field
+       SGNewCloud::cldCache->startNewFrame();
+
+       inViewClouds.clear();
+
+       // cloud fields are tiled on the flat earth
+       // compute the position in the tile
+       relx = -fmod( deltax + relative_position[SG_X], fieldSize );
+       rely = -fmod( deltay + relative_position[SG_Y], fieldSize );
+       glPushMatrix();
+       sgMat4 modelview, tmp, invtrans;
+
+       // try to find the sun position (buggy)
+       sgTransposeNegateMat4( invtrans, transform );
+    sgVec3 lightVec;
+    ssgGetLight( 0 )->getPosition( lightVec );
+    sgNegateVec3( lightVec );
+    sgXformVec3( lightVec, invtrans );
+       sgNormaliseVec3( lightVec );
+       sgCopyVec3( SGNewCloud::modelSunDir, lightVec );
+
+       // try to find the lighting data (buggy)
+       sgVec4 diffuse, ambient;
+       ssgGetLight( 0 )->getColour( GL_DIFFUSE, diffuse );
+       ssgGetLight( 0 )->getColour( GL_AMBIENT, ambient );
+       sgScaleVec3 ( SGNewCloud::sunlight, diffuse , 0.70f);
+       sgScaleVec3 ( SGNewCloud::ambLight, ambient , 0.60f);
+
+       // voodoo things on the matrix stack
+    ssgGetModelviewMatrix( modelview );
+       sgCopyMat4( tmp, transform );
+    sgPostMultMat4( tmp, modelview );
+
+       sgSetVec3( eyePos, -relx, -tmp[3][2], -rely);
+       sgSetVec3( eyePos, -relx, 0, -rely);
+       sgSetVec3( eyePos, -relx, alt, -rely);
+//     sgSetVec3( eyePos, 0, - tmp[3][2], 0);
+//     sgSetVec3( eyePos, 20000, - tmp[3][2], 20000);
+
+       tmp[3][2] = 0;
+       tmp[3][0] = 0;
+       tmp[3][1] = 0;
+    ssgLoadModelviewMatrix( tmp );
+/* flat earth
+
+       ^
+       |
+       |    FFF
+       |    FoF
+       |    FFF
+       |
+       O----------->
+               o = we are here
+               F = adjacent fields
+*/
+
+       for(int x = -1 ; x <= 1 ; x++)
+               for(int y = -1 ; y <= 1 ; y++ ) {
+                       sgVec3 fieldPos;
+                       // pretend we are not where we are
+                       sgSetVec3(fieldPos, eyePos[0] + x*fieldSize, eyePos[1], eyePos[2] + y*fieldSize);
+                       cullClouds(fieldPos, tmp);
+               }
+       // sort all visible clouds back to front (because of transparency)
+       sort( inViewClouds.begin(), inViewClouds.end() );
+       // TODO:push states
+       glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+    glEnable(GL_ALPHA_TEST);
+    glAlphaFunc(GL_GREATER, 0.0f);
+       glDisable(GL_CULL_FACE);
+//     glDisable(GL_DEPTH_TEST);
+       glEnable(GL_DEPTH_TEST);
+       glEnable(GL_SMOOTH);
+    glEnable(GL_BLEND);
+       glBlendFunc( GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
+//     glEnable( GL_COLOR_MATERIAL ); 
+       glEnable( GL_TEXTURE_2D );
+       glDisable( GL_FOG );
+    glDisable(GL_LIGHTING);
+
+       // test data: field = 11x11 cloud, 9 fields
+       // depending on position and view direction, perhaps 40 to 60 clouds not culled
+       list_of_culledCloud::iterator iCloud;
+       for( iCloud = inViewClouds.begin() ; iCloud != inViewClouds.end() ; iCloud++ ) {
+//             iCloud->aCloud->drawContainers();
+               iCloud->aCloud->Render(iCloud->eyePos);
+       }
+
+       glBindTexture(GL_TEXTURE_2D, 0);
+    glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
+
+       ssgLoadModelviewMatrix( modelview );
+
+       glPopMatrix();
+//     glEnable(GL_DEPTH_TEST);
+
+}
+
diff --git a/simgear/scene/sky/cloudfield.hxx b/simgear/scene/sky/cloudfield.hxx
new file mode 100644 (file)
index 0000000..39b0e00
--- /dev/null
@@ -0,0 +1,114 @@
+// a layer of 3d clouds
+//
+// Written by Harald JOHNSEN, started April 2005.
+//
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+//
+//
+
+#ifndef _CLOUDFIELD_HXX
+#define _CLOUDFIELD_HXX
+
+#include <plib/sg.h>
+#include <simgear/compiler.h>
+#include <vector>
+
+
+SG_USING_STD(vector);
+
+class SGNewCloud;
+
+class culledCloud {
+public:
+       SGNewCloud      *aCloud;
+       sgVec3          eyePos;
+       float           dist;
+       bool operator<(const culledCloud &b) const {
+               return (this->dist < b.dist);
+       }
+};
+typedef vector<culledCloud> list_of_culledCloud;
+
+class SGCloudField {
+
+private:
+       class Cloud  {
+       public:
+               SGNewCloud      *aCloud;
+               sgVec3          pos;
+//             float           dist;
+//             bool            culled;
+
+//             bool operator<(const Cloud &b) {
+//                     return this->dist < b.dist;
+//             }
+       };
+
+
+       typedef vector<Cloud> list_of_Cloud;
+
+       // cull all clouds of a tiled field
+       void cullClouds(sgVec3 eyePos, sgMat4 mat);
+
+       list_of_Cloud theField;
+       // this is a relative position only, with that we can move all clouds at once
+       sgVec3 relative_position;
+//     double lon, lat;
+
+       sgFrustum frustum;
+
+       sgMat4 transform;
+       double deltax, deltay, alt;
+
+public:
+
+       SGCloudField();
+       ~SGCloudField();
+
+       // add one cloud, data is not copied, ownership given
+       void addCloud( sgVec3 pos, SGNewCloud *cloud);
+
+       // for debug only
+       void buildTestLayer(void);
+
+       // Render a cloud field
+       void Render(void);
+
+       // reposition the cloud layer at the specified origin and orientation
+       void reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt);
+
+       // visibility distance for clouds in meters
+       static float CloudVis;
+
+       static float density;
+
+       static double fieldSize;
+       static bool enable3D;
+
+       // return the size of the memory pool used by texture impostors
+       static int get_CacheSize(void);
+       static float get_CloudVis(void) { return CloudVis; }
+       static float get_density(void) { return density; }
+       static bool get_enable3dClouds(void) { return enable3D; }
+
+       static void set_CacheSize(int sizeKb);
+       static void set_CloudVis(float distance);
+       static void set_density(float density);
+       static void set_enable3dClouds(bool enable);
+};
+
+#endif // _CLOUDFIELD_HXX
diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx
new file mode 100644 (file)
index 0000000..f49b7df
--- /dev/null
@@ -0,0 +1,726 @@
+// 3D cloud class
+//
+// Written by Harald JOHNSEN, started April 2005.
+//
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+//
+//
+
+#ifdef HAVE_CONFIG_H
+#  include <simgear_config.h>
+#endif
+
+#include <simgear/compiler.h>
+
+#include <plib/sg.h>
+#include <plib/ssg.h>
+#include <simgear/misc/sg_path.hxx>
+
+#include STL_ALGORITHM
+#include SG_GLUT_H
+
+#include "newcloud.hxx"
+
+
+/*
+*/
+
+static ssgTexture *cloudTextures[SGNewCloud::CLTexture_max];
+
+
+bool SGNewCloud::useAnisotropic = true;
+SGBbCache *SGNewCloud::cldCache = 0;
+static bool texturesLoaded = false;
+float SGNewCloud::nearRadius = 2500.0f;
+bool SGNewCloud::lowQuality = false;
+sgVec3 SGNewCloud::sunlight = {0.5f, 0.5f, 0.5f};
+sgVec3 SGNewCloud::ambLight = {0.5f, 0.5f, 0.5f};
+sgVec3 SGNewCloud::modelSunDir = {0,1,0};
+
+
+// constructor
+SGNewCloud::SGNewCloud() :
+       bbId(-1),
+//     rank(-1),
+       minx(999), miny(999), minz(999), maxx(-999), maxy(-999), maxz(-999)
+
+{
+       cloudId = (int) this;
+       sgSetVec3(center, 0.0f, 0.0f, 0.0f);
+       sgSetVec3(cloudpos, 0.0f, 0.0f, 0.0f);
+       list_spriteContainer.reserve(8);
+       list_spriteDef.reserve(40);
+//     if( ! texturesLoaded ) {}
+       if( cldCache == 0 ) {
+               cldCache = new SGBbCache;
+               cldCache->init( 20 );
+       }
+}
+
+SGNewCloud::~SGNewCloud() {
+       list_spriteDef.clear();
+       list_spriteContainer.clear();
+       cldCache->free( bbId, cloudId );
+}
+
+
+// load all textures used to draw cloud sprites
+void SGNewCloud::loadTextures(const string &tex_path) {
+       if( texturesLoaded )
+               return;
+       texturesLoaded = true;
+
+       SGPath cloud_path;
+
+    cloud_path.set(tex_path);
+    cloud_path.append("cl_cumulus.rgb");
+    cloudTextures[ CLTexture_cumulus ] = new ssgTexture( cloud_path.str().c_str(), false, false, false );
+    cloudTextures[ CLTexture_cumulus ]->ref();
+
+    cloud_path.set(tex_path);
+    cloud_path.append("cl_stratus.rgb");
+    cloudTextures[ CLTexture_stratus ] = new ssgTexture( cloud_path.str().c_str(), false, false, false );
+    cloudTextures[ CLTexture_stratus ]->ref();
+
+}
+
+void SGNewCloud::startFade(bool direction, float duration, float pauseLength) {
+}
+void SGNewCloud::setFade(float howMuch) {
+}
+
+
+static float rayleighCoeffAngular(float cosAngle) {
+       return 3.0f / 4.0f * (1.0f + cosAngle * cosAngle);
+}
+
+// cp is normalized (len==1)
+static void CartToPolar3d(sgVec3 cp, sgVec3 polar) {
+    polar[0] = atan2(cp[1], cp[0]);
+    polar[1] = SG_PI / 2.0f - atan2(sqrtf (cp[0] * cp[0] + cp[1] * cp[1]), cp[2]);
+       polar[2] = 1.0f;
+}
+
+static void PolarToCart3d(sgVec3 p, sgVec3 cart) {
+    float tmp = cos(p[1]);
+    cart[0] = cos(p[0]) * tmp;
+    cart[1] = sin(p[0]) * tmp;
+    cart[2] = sin(p[1]);
+}
+
+
+// compute the light for a cloud sprite corner
+// from the normal and the sun, scaled by the Rayleigh factor
+// and finaly added to the ambient light
+static void lightFunction(sgVec3 normal, sgVec4 light, float pf) {
+       float cosAngle = sgScalarProductVec3( normal, SGNewCloud::modelSunDir);
+       float vl = (1.0f - 0.1f + cosAngle / 10.0f) * pf;
+       sgScaleVec3( light, SGNewCloud::sunlight, vl );
+       sgAddVec3( light, SGNewCloud::ambLight );
+       // we need to clamp or else the light will bug when adding transparency
+       if( light[0] > 1.0 )    light[0] = 1.0;
+       if( light[1] > 1.0 )    light[1] = 1.0;
+       if( light[2] > 1.0 )    light[2] = 1.0;
+       light[3] = 1.0;
+}
+
+// compute the light for a cloud sprite
+// we use ambient light and orientation versus sun position
+// TODO:check sun pos and check code
+void SGNewCloud::computeSimpleLight(sgVec3 FakeEyePos) {
+       // constant Rayleigh factor if we are not doing Anisotropic lighting
+       float pf = 1.0f;
+       const float ang = 45.0f * SG_PI / 180.0f;
+       list_of_spriteDef::iterator iSprite;
+       for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
+               if( useAnisotropic ) {
+                       sgVec3 eyeDir;
+            sgSubVec3(eyeDir, iSprite->pos, FakeEyePos);
+            sgNormaliseVec3(eyeDir);
+            float cosAngle = sgScalarProductVec3(eyeDir, modelSunDir);
+            pf = rayleighCoeffAngular(cosAngle);
+               }
+               // compute the vector going from the container box center to the sprite
+               // TODO : this is a constant except for cloudpos, compute the normal in setpos function
+               sgVec3 normal;
+               spriteContainer *thisSpriteContainer = &list_spriteContainer[iSprite->box];
+        sgSubVec3(normal, iSprite->pos, thisSpriteContainer->pos);
+        sgSubVec3(normal, thisSpriteContainer->center);
+        sgSubVec3(normal, cloudpos);
+        sgNormaliseVec3(normal);
+               if( lowQuality ) {
+                       // juste use the traditional normal to compute some lightning
+                       sgVec4 centerColor;
+                       lightFunction(normal, centerColor, pf);
+                       sgCopyVec4(iSprite->l0, centerColor);
+                       sgCopyVec4(iSprite->l1, centerColor);
+                       sgCopyVec4(iSprite->l2, centerColor);
+                       sgCopyVec4(iSprite->l3, centerColor);
+
+               } else {
+                       // use exotic lightning function, this will give more 'relief' to the clouds
+                       // compute a normal for each vextex this will simulate a smooth shading for a round shape
+                       sgVec3 polar, cart, pt;
+                       // I suspect this code to be bugged...
+            CartToPolar3d(normal, polar);
+
+                       // offset the normal vector by some angle for each vertex
+            sgSetVec3(pt, polar[0] - ang, polar[1] - ang, polar[2]);
+            PolarToCart3d(pt, cart);
+            lightFunction(cart, iSprite->l0, pf);
+            sgSetVec3(pt, polar[0] + ang, polar[1] - ang, polar[2]);
+            PolarToCart3d(pt, cart);
+            lightFunction(cart, iSprite->l1, pf);
+            sgSetVec3(pt, polar[0] + ang, polar[1] + ang, polar[2]);
+            PolarToCart3d(pt, cart);
+            lightFunction(cart, iSprite->l2, pf);
+            sgSetVec3(pt, polar[0] - ang, polar[1] + ang, polar[2]);
+            PolarToCart3d(pt, cart);
+            lightFunction(cart, iSprite->l3, pf);
+               }
+       }
+}
+
+
+// add a new box to the cloud
+void SGNewCloud::addContainer (float x, float y, float z, float r, CLbox_type type) {
+       spriteContainer cont;
+       sgSetVec3( cont.pos, x, y, z );
+       cont.r = r;
+       cont.cont_type = type;
+       sgSetVec3( cont.center, 0.0f, 0.0f, 0.0f);
+       list_spriteContainer.push_back( cont );
+}
+
+// add a sprite inside a box
+void SGNewCloud::addSprite(float x, float y, float z, float r, CLbox_type type, int box) {
+       spriteDef newSpriteDef;
+       int rank = list_spriteDef.size();
+       sgSetVec3( newSpriteDef.pos, x, y, z);
+       newSpriteDef.box = box;
+       newSpriteDef.sprite_type = type;
+       newSpriteDef.rank = rank;
+       newSpriteDef.r = r;
+       list_spriteDef.push_back( newSpriteDef );
+       spriteContainer *thisBox = &list_spriteContainer[box];
+       sgVec3 deltaPos;
+       sgSubVec3( deltaPos, newSpriteDef.pos, thisBox->pos );
+       sgAddVec3( thisBox->center, deltaPos );
+
+       r = r * 0.6f;   // 0.5 * 1.xxx
+    if( x - r < minx )
+               minx = x - r;
+    if( y - r < miny )
+               miny = y - r;
+    if( z - r < minz )
+               minz = z - r;
+    if( x + r > maxx )
+               maxx = x + r;
+    if( y + r > maxy )
+               maxy = y + r;
+    if( z + r > maxz )
+               maxz = z + r;
+
+}
+
+// return a random number between -n/2 and n/2
+static float Rnd(float n) {
+       return n * (-0.5f + rand() / (float) RAND_MAX);
+}
+
+// generate all sprite with defined boxes
+void SGNewCloud::genSprites( void ) {
+       float x, y, z, r;
+    int N, sc;
+    N = list_spriteContainer.size();
+       for(int i = 0 ; i < N ; i++ ) {
+               spriteContainer *thisBox = & list_spriteContainer[i];
+               // the type defines how the sprites can be positioned inside the box, their size, etc
+               switch(thisBox->cont_type) {
+                       case CLbox_sc:
+                               for( sc = 0 ; sc <= 4 ; sc ++ ) {
+                                       r = thisBox->r + Rnd(0.2f);
+                                       x = thisBox->pos[SG_X] + Rnd(thisBox->r);
+                                       y = thisBox->pos[SG_Y] + Rnd(thisBox->r * 0.2f);
+                                       z = thisBox->pos[SG_Z] + Rnd(thisBox->r);
+                                       addSprite(x, y, z, r, thisBox->cont_type, i);
+                               }
+                               break;
+                       case CLbox_stratus:
+                               sc = 1;
+                               r = thisBox->r;
+                               x = thisBox->pos[SG_X];
+                               y = thisBox->pos[SG_Y];
+                               z = thisBox->pos[SG_Z];
+                               addSprite(x, y, z, r, thisBox->cont_type, i);
+                               break;
+                       case CLbox_cumulus:
+                               for( sc = 0 ; sc <= 4 ; sc ++ ) {
+                                       r = thisBox->r + Rnd(0.2f);
+                                       x = thisBox->pos[SG_X] + Rnd(thisBox->r * 0.75f);
+                                       y = thisBox->pos[SG_Y] + Rnd(thisBox->r * 0.5f);
+                                       z = thisBox->pos[SG_Z] + Rnd(thisBox->r * 0.75f);
+                                       if ( y < thisBox->pos[SG_Y] - thisBox->r / 10.0f )
+                                               y = thisBox->pos[SG_Y] - thisBox->r / 10.0f;
+                                       addSprite(x, y, z, r, thisBox->cont_type, i);
+                               }
+                               break;
+                       default:
+                               for( sc = 0 ; sc <= 4 ; sc ++ ) {
+                                       r = thisBox->r + Rnd(0.2f);
+                                       x = thisBox->pos[SG_X] + Rnd(thisBox->r);
+                                       y = thisBox->pos[SG_Y] + Rnd(thisBox->r);
+                                       z = thisBox->pos[SG_Z] + Rnd(thisBox->r);
+                                       addSprite(x, y, z, r, thisBox->cont_type, i);
+                               }
+                               break;
+               }
+        sgScaleVec3(thisBox->center, 1.0f / sc);
+       }
+
+       radius = maxx - minx;
+    if ( (maxy - miny) > radius )
+               radius = (maxy - miny);
+    if ( (maxz - minz) > radius )
+               radius = (maxz - minz);
+    radius /= 2.0f;
+    sgSetVec3( center, (maxx + minx) / 2.0f, (maxy + miny) / 2.0f, (maxz + minz) / 2.0f );
+
+/*    fadingrank = 0
+'    fadingrank = UBound(tbSpriteDef()) * 10
+    fadingdir = 0*/
+       // TODO : compute initial sprite normals for lighting function
+}
+
+
+// definition of a cu cloud, only for testing
+void SGNewCloud::new_cu(void) {
+       float s = 150.0f;
+       float r = Rnd(1.0) + 0.5;
+       if( r < 0.5f ) {
+               addContainer(0.0f, 0.0f, 0.0f, s, CLbox_cumulus);
+               addContainer(s, 0, 0, s, CLbox_cumulus);
+               addContainer(0, 0, 2 * s, s, CLbox_cumulus);
+               addContainer(s, 0, 2 * s, s, CLbox_cumulus);
+
+               addContainer(-1.2f * s, 0.2f * s, s, s * 1.4f, CLbox_cumulus);
+               addContainer(0.2f * s, 0.2f * s, s, s * 1.4f, CLbox_cumulus);
+               addContainer(1.6f * s, 0.2f * s, s, s * 1.4f, CLbox_cumulus);
+       } else if ( r < 0.90f ) {
+               addContainer(0, 0, 0, s * 1.2, CLbox_cumulus);
+               addContainer(s, 0, 0, s, CLbox_cumulus);
+               addContainer(0, 0, s, s, CLbox_cumulus);
+               addContainer(s * 1.1, 0, s, s * 1.2, CLbox_cumulus);
+
+               addContainer(-1.2 * s, 1 + 0.2 * s, s * 0.5, s * 1.4, CLbox_standard);
+               addContainer(0.2 * s, 1 + 0.25 * s, s * 0.5, s * 1.5, CLbox_standard);
+               addContainer(1.6 * s, 1 + 0.2 * s, s * 0.5, s * 1.4, CLbox_standard);
+
+       } else {
+               // cb
+               s = 475.0f;
+               addContainer(0, 0, 0, s, CLbox_cumulus);
+               addContainer(0, 0, s, s, CLbox_cumulus);
+               addContainer(s, 0, s, s, CLbox_cumulus);
+               addContainer(s, 0, 0, s, CLbox_cumulus);
+
+               addContainer(s / 2, s, s / 2, s * 1.5, CLbox_standard);
+
+               addContainer(0, 2 * s, 0, s, CLbox_standard);
+               addContainer(0, 2 * s, s, s, CLbox_standard);
+               addContainer(s, 2 * s, s, s, CLbox_standard);
+               addContainer(s, 2 * s, 0, s, CLbox_standard);
+
+       }
+       genSprites();
+}
+
+
+// define the new position of the cloud (inside the cloud field, not on sphere)
+void SGNewCloud::SetPos(sgVec3 newPos) {
+    int N = list_spriteDef.size();
+    sgVec3 deltaPos;
+       sgSubVec3( deltaPos, newPos, cloudpos );
+
+    // for each particle
+       for(int i = 0 ; i < N ; i ++) {
+               sgAddVec3( list_spriteDef[i].pos, deltaPos );
+       }
+       sgAddVec3( center, deltaPos );
+    sgSetVec3( cloudpos, newPos[SG_X], newPos[SG_Y], newPos[SG_Z]);
+       // TODO : recompute sprite normal so we don't have to redo that each frame
+}
+
+
+
+
+/*
+Public Sub drawContainers()
+    Dim N As Integer, i As Integer
+    N = UBound(tbSpriteCont())
+
+    Call glPolygonMode(faceFrontAndBack, pgmLine)
+
+    Call glColor3f(0.9, 0.9, 0.9)
+
+    For i = 0 To N - 1
+        With tbSpriteCont(i)
+            Call glPushMatrix
+            Call glTranslatef(.x * c_scale + cloudpos(0), .y * c_scale + cloudpos(1), .z * c_scale + cloudpos(2))
+            Call gCtl.Shapes.box(.r * c_scale, .r * c_scale, .r * c_scale)
+            Call glPopMatrix
+        End With
+    Next i
+If 0 Then
+    Call glPushMatrix
+        Call glTranslatef(ccenter(0), ccenter(1), ccenter(2))
+        Call gCtl.Shapes.Sphere(cradius, 8, 8)
+    Call glPopMatrix
+End If
+End Sub
+*/
+void SGNewCloud::drawContainers() {
+
+
+}
+
+
+/*
+*/
+
+//bool SGNewCloud::compareSpriteFunction(const spriteDef &a, const spriteDef &b) {
+//     return (a.dist > b.dist);
+//}
+
+// sort on distance to eye because of transparency
+void SGNewCloud::sortSprite( sgVec3 eye ) {
+       list_of_spriteDef::iterator iSprite;
+
+       // compute distance from sprite to eye
+       for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
+               sgVec3 dist;
+               sgSubVec3( dist, iSprite->pos, eye );
+               iSprite->dist = -(dist[0]*dist[0] + dist[1]*dist[1] + dist[2]*dist[2]);
+       }
+       sort( list_spriteDef.begin(), list_spriteDef.end() );
+}
+
+// render the cloud on screen or on the RTT texture to build the impostor
+void SGNewCloud::Render3Dcloud( bool drawBB, sgVec3 FakeEyePos, sgVec3 deltaPos, float dist_center ) {
+
+/*    int clrank                = fadingrank / 10;
+       int clfadeinrank = fadingrank - clrank * 10;*/
+
+
+       GLint previousTexture = -1, thisTexture;
+       list_of_spriteDef::iterator iSprite;
+       for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
+               // choose texture to use depending on sprite type
+               switch(iSprite->sprite_type) {
+                       case CLbox_stratus:
+                               thisTexture = CLTexture_stratus;
+                               break;
+                       default:
+                               thisTexture = CLTexture_cumulus;
+                               break;
+               }
+               // in practice there is no texture switch (atm)
+               if( previousTexture != thisTexture ) {
+                       previousTexture = thisTexture;
+                       glBindTexture(GL_TEXTURE_2D, cloudTextures[thisTexture]->getHandle());
+               }
+
+                       sgVec3 translate;
+                       if( drawBB ) {
+                               sgCopyVec3( translate, iSprite->pos);
+                               sgSubVec3( translate, iSprite->pos, deltaPos );
+                       }
+                       else
+                               sgSubVec3( translate, iSprite->pos, deltaPos);
+
+
+                       // flipx and flipy are random texture flip flags, this gives more random clouds
+                       float flipx = (float) ( iSprite->rank & 1 );
+                       float flipy = (float) ( (iSprite->rank >> 1) & 1 );
+                       // cu texture have a flat bottom so we can't do a vertical flip
+                       if( iSprite->sprite_type == CLbox_cumulus || iSprite->sprite_type == CLbox_stratus )
+                               flipy = 0.0f;
+                       if( iSprite->sprite_type == CLbox_stratus )
+                               flipx = 0.0f;
+                       // adjust colors depending on cloud type
+                       // TODO : rewrite that later, still experimental
+                       switch(iSprite->sprite_type) {
+                               case CLbox_cumulus:
+                                       // dark bottom
+                    sgScaleVec3(iSprite->l0, 0.6f);
+                    sgScaleVec3(iSprite->l1, 0.6f);
+                                       break;
+                               case CLbox_stratus:
+                                       // usually dark grey
+                    sgScaleVec3(iSprite->l0, 0.8f);
+                    sgScaleVec3(iSprite->l1, 0.8f);
+                    sgScaleVec3(iSprite->l2, 0.8f);
+                    sgScaleVec3(iSprite->l3, 0.8f);
+                                       break;
+                               default:
+                                       // darker bottom than top
+                    sgScaleVec3(iSprite->l0, 0.8f);
+                    sgScaleVec3(iSprite->l1, 0.8f);
+                                       break;
+                       }
+                       float r = iSprite->r * 0.5f;
+
+                       sgVec4 l0, l1, l2, l3;
+                       sgCopyVec4 ( l0, iSprite->l0 );
+                       sgCopyVec4 ( l1, iSprite->l1 );
+                       sgCopyVec4 ( l2, iSprite->l2 );
+                       sgCopyVec4 ( l3, iSprite->l3 );
+                       if( ! drawBB ) {
+                               // blend clouds with sky based on distance to limit the contrast of distant cloud
+                               // TODO:use cloudfield vis, not hardcoded value
+                               float t = 1.0f - dist_center / (15000.0f * 2.0 );
+                               if ( t < 0.0f ) 
+                                       t = 0.0f;       // no, it should have been culled
+                               // now clouds at the far plane are half blended
+                               sgScaleVec4( l0, t );
+                               sgScaleVec4( l1, t );
+                               sgScaleVec4( l2, t );
+                               sgScaleVec4( l3, t );
+                       }
+                       // compute the rotations so that the quad is facing the camera
+                       sgVec3 pos;
+                       sgSetVec3( pos, translate[SG_X], translate[SG_Z], translate[SG_Y] );
+                       sgCopyVec3( translate, pos );
+                       sgNormaliseVec3( translate );
+                       sgVec3 x, y, up = {0.0f, 0.0f, 1.0f};
+                       sgVectorProductVec3(x, translate, up);
+                       sgNormaliseVec3(x);
+                       sgScaleVec3(x, r);
+                       sgVectorProductVec3(y, x, translate);
+                       sgNormaliseVec3(y);
+                       sgScaleVec3(y, r);
+                       sgVec3 left, right;
+                       if( drawBB )
+                               sgSetVec3( left, iSprite->pos[SG_X], iSprite->pos[SG_Z], iSprite->pos[SG_Y]);
+                       else
+                               sgCopyVec3( left, pos );
+                       sgSubVec3 (left, y);
+                       sgAddVec3 (right, left, x);
+                       sgSubVec3 (left, x);
+
+                       glBegin(GL_QUADS);
+                               glColor4fv(l0);
+                glTexCoord2f(flipx, 1.0f - flipy);
+                glVertex3fv(left);
+                glColor4fv(l1);
+                glTexCoord2f(1.0f - flipx, 1.0f - flipy);
+                glVertex3fv(right);
+                               sgScaleVec3( y, 2.0 );
+                               sgAddVec3( left, y);
+                               sgAddVec3( right, y);
+                glColor4fv(l2);
+                glTexCoord2f(1.0f - flipx, flipy);
+                glVertex3fv(right);
+                glColor4fv(l3);
+                glTexCoord2f(flipx, flipy);
+                glVertex3fv(left);
+
+                       glEnd();        
+
+       }
+}
+
+
+// compute rotations so that a quad is facing the camera
+// TODO:change obsolete code because we dont use glrotate anymore
+void SGNewCloud::CalcAngles(sgVec3 refpos, sgVec3 FakeEyePos, float *angleY, float *angleX) {
+    sgVec3 upAux, lookAt, objToCamProj, objToCam;
+       float angle, angle2;
+
+    sgSetVec3(objToCamProj, -FakeEyePos[SG_X] + refpos[SG_X], -FakeEyePos[SG_Z] + refpos[SG_Z], 0.0f);
+    sgNormaliseVec3(objToCamProj);
+
+    sgSetVec3(lookAt, 0.0f, 1.0f, 0.0f);
+    sgVectorProductVec3(upAux, lookAt, objToCamProj);
+    angle = sgScalarProductVec3(lookAt, objToCamProj);
+       if( (angle < 0.9999f) && (angle > -0.9999f) ) {
+        angle = acos(angle) * 180.0f / SG_PI;
+        if( upAux[2] < 0.0f )
+            angle = -angle;
+       } else
+        angle = 0.0f;
+
+    sgSetVec3(objToCam, -FakeEyePos[SG_X] + refpos[SG_X], -FakeEyePos[SG_Z] + refpos[SG_Z], -FakeEyePos[SG_Y] + refpos[SG_Y]);
+       sgNormaliseVec3(objToCam);
+
+    angle2 = sgScalarProductVec3(objToCamProj, objToCam);
+       if( (angle2 < 0.9999f) && (angle2 > -0.9999f) ) {
+        angle2 = -acos(angle2) * 180.0f / SG_PI;
+        if(  objToCam[2] > 0.0f )
+            angle2 = -angle2;
+       } else
+        angle2 = 0.0f;
+
+       angle2 += 90.0f;
+
+       *angleY = angle;
+    *angleX = angle2;
+}
+
+// draw a cloud but this time we use the impostor texture
+void SGNewCloud::RenderBB(sgVec3 deltaPos, float angleY, float angleX, float dist_center) {
+       // TODO:glrotate is not needed
+    glPushMatrix();
+        glTranslatef(center[SG_X] - deltaPos[SG_X], center[SG_Z] - deltaPos[SG_Z], center[SG_Y] - deltaPos[SG_Y]);
+        glRotatef(angleY, 0.0f, 0.0f, 1.0f);
+        glRotatef(angleX, 1.0f, 0.0f, 0.0f);
+               // blend clouds with sky based on distance to limit the contrast of distant cloud
+               // TODO:use cloudfield vis, not hardcoded value
+               float t = 1.0f - dist_center / (15000.0f * 2.0 );
+               // err the alpha value is not good for impostor, debug that
+               t *= 1.65;
+               if ( t < 0.0f ) 
+                       t = 0.0f;
+  
+        glColor4f(t, t, t, t);
+        float r = radius;
+               glBegin(GL_QUADS);
+                       glTexCoord2f(0.0f, 1.0f);
+                       glVertex2f(-r, r);
+                       glTexCoord2f(1.0f, 1.0f);
+                       glVertex2f(r, r);
+                       glTexCoord2f(1.0f, 0.0f);
+                       glVertex2f(r, -r);
+                       glTexCoord2f(0.0f, 0.0f);
+                       glVertex2f(-r, -r);
+               glEnd();
+
+#if 1  // debug only
+               int age = cldCache->queryImpostorAge(bbId);
+               // draw a red border for the newly generated BBs else draw a white border
+        if( age < 200 )
+            glColor3f(1, 0, 0);
+               else
+            glColor3f(1, 1, 1);
+
+        glBindTexture(GL_TEXTURE_2D, 0);
+        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+        glBegin(GL_QUADS);
+            glVertex2f(-r, -r);
+            glVertex2f(r, -r);
+            glVertex2f(r, r);
+            glVertex2f(-r, r);
+        glEnd();
+               glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+
+#endif
+
+       glPopMatrix();
+
+}
+
+// determine if it is a good idea to use an impostor to render the cloud
+bool SGNewCloud::isBillboardable(float dist) {
+
+       if( dist <= ( 2.1f * radius ) ) {
+        // inside cloud
+        return false;
+       }
+       if( (dist-radius) <= nearRadius ) {
+        // near clouds we don't want to use BB
+        return false;
+       }
+//     return false;
+       return true;
+}
+
+
+
+// render the cloud, fakepos is a relative position inside the cloud field
+void SGNewCloud::Render(sgVec3 fakepos) {
+       sgVec3 eyePos, FakeEyePos;
+       sgVec3 dist;
+
+
+       glColor3f(1.0f, 1.0f, 1.0f);
+
+       // obsolete code
+       sgCopyVec3( eyePos, fakepos );
+
+    sgCopyVec3( FakeEyePos, fakepos);
+       sgVec3 deltaPos;
+//    sgSubVec3( deltaPos, eyePos, FakeEyePos);
+       sgCopyVec3( deltaPos, FakeEyePos);
+    sgSubVec3( dist, center, FakeEyePos);
+    float dist_center = sgLengthVec3(dist);
+
+       // eeeek don't do that so early, perhaps we will use an impostor
+    computeSimpleLight( FakeEyePos );
+
+    // view point sort, we sort because of transparency
+       // eeeek don't do that so early, perhaps we will use an impostor
+       sortSprite( FakeEyePos );
+
+
+       if( !isBillboardable(dist_center) ) {
+               // not a good candidate for impostors, draw a real cloud
+               Render3Dcloud(false, FakeEyePos, deltaPos, dist_center);
+       } else {
+               GLuint texID = 0;
+                       // lets use our impostor
+                       if( bbId >= 0)
+                               texID = cldCache->QueryTexID(cloudId, bbId);
+
+                       // ok someone took our impostor, so allocate a new one
+                       if( texID == 0 ) {
+                // allocate a new Impostor
+                bbId = cldCache->alloc(cloudId);
+                               texID = cldCache->QueryTexID(cloudId, bbId);
+                       }
+                       if( texID == 0 ) {
+                // no more free texture in the pool
+                Render3Dcloud(false, FakeEyePos, deltaPos, dist_center);
+                       } else {
+                float angleX, angleY;
+                CalcAngles(center, FakeEyePos, &angleY, &angleX);
+                               if( ! cldCache->isBbValid( cloudId, bbId, angleY, angleX) ) {
+                    // we must build or rebuild this billboard
+                                       // start render to texture
+                    cldCache->beginCapture();
+                                       // set transformation matrices
+                    cldCache->setRadius(radius, dist_center);
+                                       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);
+                                       // draw into texture
+                    Render3Dcloud(true, FakeEyePos, deltaPos, dist_center);
+                                       // save rotation angles for later use
+                                       // TODO:this is not ok
+                                       cldCache->setReference(cloudId, bbId, angleY, angleX);
+                                       // save the rendered cloud into the cache
+                                       cldCache->setTextureData( bbId );
+                                       // finish render to texture and go back into standard context
+                    cldCache->endCapture();
+                               }
+                // draw the newly built BB or an old one
+                glBindTexture(GL_TEXTURE_2D, texID);
+                RenderBB(deltaPos, angleY, angleX, dist_center);
+                       }
+       }
+
+}
+
diff --git a/simgear/scene/sky/newcloud.hxx b/simgear/scene/sky/newcloud.hxx
new file mode 100644 (file)
index 0000000..6a789c0
--- /dev/null
@@ -0,0 +1,147 @@
+// 3D cloud class
+//
+// Written by Harald JOHNSEN, started April 2005.
+//
+// Copyright (C) 2005  Harald JOHNSEN - hjohnsen@evc.net
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
+//
+//
+
+#ifndef _NEWCLOUD_HXX
+#define _NEWCLOUD_HXX
+
+#include <plib/sg.h>
+#include <simgear/compiler.h>
+#include STL_STRING
+#include <vector>
+
+#include "bbcache.hxx"
+
+SG_USING_STD(string);
+SG_USING_STD(vector);
+
+class SGNewCloud {
+
+public:
+       SGNewCloud();
+       ~SGNewCloud();
+
+       enum CLbox_type {
+               CLbox_standard = 0,
+               CLbox_sc = 1,
+               CLbox_cumulus = 2,
+               CLbox_stratus = 3
+       };
+
+       enum CLTexture_type {
+               CLTexture_cumulus = 1,
+               CLTexture_stratus = 2,
+               CLTexture_max
+       };
+private:
+
+       class spriteDef {
+       public:
+               sgVec3          pos;
+               float           r;
+               CLbox_type      sprite_type;
+               sgVec4          l0, l1, l2, l3;
+               int                     rank;
+               int                     box;
+               float           dist;           // distance used during sort
+               bool operator<(const spriteDef &b) const {
+                       return this->dist < b.dist;
+               }
+       };
+
+       typedef struct {
+               sgVec3          pos;
+               float           r;
+               // the type defines how the sprites can be positioned inside the box, their size, etc
+               CLbox_type      cont_type;
+               sgVec3          center;
+       } spriteContainer;
+
+       typedef vector<spriteDef>               list_of_spriteDef;
+       typedef vector<spriteContainer> list_of_spriteContainer;
+
+       void computeSimpleLight(sgVec3 eyePos);
+       void addSprite(float x, float y, float z, float r, CLbox_type type, int box);
+
+       // sort on distance to eye because of transparency
+       void sortSprite( sgVec3 eyePos );
+
+       // render the cloud on screen or on the RTT texture to build the impostor
+       void Render3Dcloud( bool drawBB, sgVec3 eyePos, sgVec3 deltaPos, float dist_center );
+
+       // compute rotations so that a quad is facing the camera
+       void CalcAngles(sgVec3 refpos, sgVec3 eyePos, float *angleY, float *angleX);
+
+       // draw a cloud but this time we use the impostor texture
+       void RenderBB(sgVec3 deltaPos, float angleY, float angleX, float dist_center);
+
+       // determine if it is a good idea to use an impostor to render the cloud
+       bool isBillboardable(float dist);
+
+       int             cloudId, bbId;
+//     int             rank;
+       sgVec3  cloudpos, center;
+       list_of_spriteDef               list_spriteDef;
+       list_of_spriteContainer list_spriteContainer;
+       float minx, maxx, miny, maxy, minz, maxz;
+       float radius;
+
+       // fading data
+       bool direction, fadeActive;
+       float duration, pauseLength;
+       // need timer here
+
+public:
+       // add a new box to the cloud
+       void addContainer(float x, float y, float z, float r, CLbox_type type);
+
+       // generate all sprite with defined boxes
+       void genSprites(void);
+
+       // debug only, define a cumulus
+       void new_cu(void);
+
+       // debug only
+       void drawContainers(void);
+
+       // define the new position of the cloud (inside the cloud field, not on sphere)
+       void SetPos(sgVec3 newPos);
+
+       // render the cloud, fakepos is a relative position inside the cloud field
+       void Render(sgVec3 fakepos);
+
+       // 
+       void startFade(bool direction, float duration, float pauseLength);
+       void setFade(float howMuch);
+
+       inline float getRadius() { return radius; }
+
+       // load all textures used to draw cloud sprites
+       static void loadTextures( const string &tex_path );
+       static sgVec3 modelSunDir;
+       static sgVec3 sunlight, ambLight;
+       static bool useAnisotropic;
+       static float nearRadius;
+       static bool lowQuality;
+       static SGBbCache        *cldCache;
+};
+
+#endif // _NEWCLOUD_HXX