From: ehofman Date: Sun, 24 Apr 2005 11:16:50 +0000 (+0000) Subject: Harald Johnson: X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=b5e03030d1d9549104a936242f602a9a27198d01;p=simgear.git Harald Johnson: - 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 --- diff --git a/simgear/environment/Makefile.am b/simgear/environment/Makefile.am index 1948696e..5505aa5d 100644 --- a/simgear/environment/Makefile.am +++ b/simgear/environment/Makefile.am @@ -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 index 00000000..0fdeae26 --- /dev/null +++ b/simgear/environment/visual_enviro.cxx @@ -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 +#include +#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 index 00000000..7af67e09 --- /dev/null +++ b/simgear/environment/visual_enviro.cxx~ @@ -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 +#include +#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.hxx b/simgear/environment/visual_enviro.hxx new file mode 100644 index 00000000..cc2a65d5 --- /dev/null +++ b/simgear/environment/visual_enviro.hxx @@ -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 index 00000000..1af1dce9 --- /dev/null +++ b/simgear/environment/visual_enviro.hxx~ @@ -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/scene/sky/Makefile.am b/simgear/scene/sky/Makefile.am index efc38b9a..177633dc 100644 --- a/simgear/scene/sky/Makefile.am +++ b/simgear/scene/sky/Makefile.am @@ -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 index 00000000..ad253cda --- /dev/null +++ b/simgear/scene/sky/bbcache.cxx @@ -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 +#endif + +#include + +#include +#include +#include +#include +#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 index 00000000..f2ba5641 --- /dev/null +++ b/simgear/scene/sky/bbcache.hxx @@ -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 +#include +#include +#include + +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 diff --git a/simgear/scene/sky/cloud.cxx b/simgear/scene/sky/cloud.cxx index 24b03e96..776044a3 100644 --- a/simgear/scene/sky/cloud.cxx +++ b/simgear/scene/sky/cloud.cxx @@ -39,6 +39,8 @@ inline int (isnan)(double r) { return isnan(r); } #include #include +#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, diff --git a/simgear/scene/sky/cloud.hxx b/simgear/scene/sky/cloud.hxx index dbd7c8fb..9d0f139e 100644 --- a/simgear/scene/sky/cloud.hxx +++ b/simgear/scene/sky/cloud.hxx @@ -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 index 00000000..a847c57a --- /dev/null +++ b/simgear/scene/sky/cloudfield.cxx @@ -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 +#endif + +#include + +#include +#include +#include + +#include STL_ALGORITHM +#include SG_GLUT_H +#include + +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 index 00000000..39b0e001 --- /dev/null +++ b/simgear/scene/sky/cloudfield.hxx @@ -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 +#include +#include + + +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 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 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 index 00000000..f49b7df5 --- /dev/null +++ b/simgear/scene/sky/newcloud.cxx @@ -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 +#endif + +#include + +#include +#include +#include + +#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 index 00000000..6a789c0d --- /dev/null +++ b/simgear/scene/sky/newcloud.hxx @@ -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 +#include +#include STL_STRING +#include + +#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 list_of_spriteDef; + typedef vector 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