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)
--- /dev/null
+// 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) {
+}
+
--- /dev/null
+// 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
--- /dev/null
+// 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
+
--- /dev/null
+// 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
+
oursun.hxx \
sky.hxx \
sphere.hxx \
- stars.hxx
+ stars.hxx \
+ bbcache.hxx \
+ cloudfield.hxx \
+ newcloud.hxx
libsgsky_a_SOURCES = \
cloud.cxx \
oursun.cxx \
sky.cxx \
sphere.cxx \
- stars.cxx
+ stars.cxx \
+ bbcache.cxx \
+ cloudfield.cxx \
+ newcloud.cxx
INCLUDES = -I$(top_srcdir)
--- /dev/null
+// 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;
+ }
+}
--- /dev/null
+// 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
#include <simgear/screen/extensions.hxx>
#include <simgear/screen/texture.hxx>
+#include "newcloud.hxx"
+#include "cloudfield.hxx"
#include "cloud.hxx"
#if defined(__MINGW32__)
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
layer_states[SG_CLOUD_CLEAR] = 0;
}
+ SGNewCloud::loadTextures(texture_path.str());
+ layer3D->buildTestLayer();
}
if ( bump_mapping ) {
last_lat = lat;
}
+ layer3D->reposition( p, up, lon, lat, alt, dt);
return true;
}
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,
// SG_USING_STD(cout);
// SG_USING_STD(endl);
+class SGCloudField;
/**
* A class layer to model a single cloud layer
// position, not view position
// double xoff, yoff;
double last_lon, last_lat, last_course;
+
+ SGCloudField *layer3D;
};
--- /dev/null
+// 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);
+
+}
+
--- /dev/null
+// 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
--- /dev/null
+// 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);
+ }
+ }
+
+}
+
--- /dev/null
+// 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