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
This commit is contained in:
ehofman
2005-04-24 11:16:50 +00:00
parent 65056bfa72
commit b5e03030d1
14 changed files with 2071 additions and 4 deletions

View File

@@ -2,8 +2,8 @@ includedir = @includedir@/environment
lib_LIBRARIES = libsgenvironment.a
include_HEADERS = metar.hxx
include_HEADERS = metar.hxx visual_enviro.hxx
libsgenvironment_a_SOURCES = metar.cxx
libsgenvironment_a_SOURCES = metar.cxx visual_enviro.cxx
INCLUDES = -I$(top_srcdir)

View File

@@ -0,0 +1,81 @@
// Visual environment helper class
//
// Written by Harald JOHNSEN, started April 2005.
//
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
//
//
#include <simgear/scene/sky/cloudfield.hxx>
#include <simgear/scene/sky/newcloud.hxx>
#include "visual_enviro.hxx"
SGEnviro sgEnviro;
SGEnviro::SGEnviro(void) {
}
SGEnviro::~SGEnviro(void) {
}
int SGEnviro::get_clouds_CacheSize(void) const {
return SGCloudField::get_CacheSize();
}
float SGEnviro::get_clouds_visibility(void) const {
return SGCloudField::get_CloudVis();
}
float SGEnviro::get_clouds_density(void) const {
return SGCloudField::get_density();
}
bool SGEnviro::get_clouds_enable_state(void) const {
return SGCloudField::get_enable3dClouds();
}
void SGEnviro::set_clouds_CacheSize(int sizeKb) {
SGCloudField::set_CacheSize(sizeKb);
}
void SGEnviro::set_clouds_visibility(float distance) {
SGCloudField::set_CloudVis(distance);
}
void SGEnviro::set_clouds_density(float density) {
SGCloudField::set_density(density);
}
void SGEnviro::set_clouds_enable_state(bool enable) {
SGCloudField::set_enable3dClouds(enable);
}
// rain/snow
float SGEnviro::get_precipitation_density(void) const {
return 0.0;
}
bool SGEnviro::get_precipitation_enable_state(void) const {
return false;
}
void SGEnviro::set_precipitation_density(float density) {
}
void SGEnviro::set_precipitation_enable_state(bool enable) {
}
// others
bool SGEnviro::get_lightning_enable_state(void) const {
return false;
}
void SGEnviro::set_lightning_enable_state(bool enable) {
}

View File

@@ -0,0 +1,81 @@
// Visual environment helper class
//
// Written by Harald JOHNSEN, started April 2005.
//
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
//
//
#include <simgear/scene/sky/cloudfield.hxx>
#include <simgear/scene/sky/newcloud.hxx>
#include "visual_enviro.hxx"
SGEnviro sgEnviro;
SGEnviro::SGEnviro(void) {
}
SGEnviro::~SGEnviro(void) {
}
int SGEnviro::get_clouds_CacheSize(void) const {
return SGCloudField::get_CacheSize();
}
float SGEnviro::get_clouds_visibility(void) const {
return SGCloudField::get_CloudVis();
}
float SGEnviro::get_clouds_density(void) const {
return SGCloudField::get_density();
}
bool SGEnviro::get_clouds_enable_state(void) const {
return SGCloudField::get_enable3dClouds();
}
void SGEnviro::set_clouds_CacheSize(int sizeKb) {
SGCloudField::set_CacheSize(sizeKb);
}
void SGEnviro::set_clouds_visibility(float distance) {
SGCloudField::set_CloudVis(distance);
}
void SGEnviro::set_clouds_density(float density) {
SGCloudField::set_density(density);
}
void SGEnviro::set_clouds_enable_state(bool enable) {
SGCloudField::set_enable3dClouds(enable);
}
// rain/snow
float SGEnviro::get_precipitation_density(void) const {
return 0.0;
}
bool SGEnviro::get_precipitation_enable_state(void) const {
return false;
}
void SGEnviro::set_precipitation_density(float density) {
}
void SGEnviro::set_precipitation_enable_state(bool enable) {
}
// others
bool SGEnviro::get_lightning_enable_state(void) const {
return false;
}
void SGEnviro::set_lightning_enable_state(bool enable) {
}

View File

@@ -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

View File

@@ -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

View File

@@ -11,7 +11,10 @@ include_HEADERS = \
oursun.hxx \
sky.hxx \
sphere.hxx \
stars.hxx
stars.hxx \
bbcache.hxx \
cloudfield.hxx \
newcloud.hxx
libsgsky_a_SOURCES = \
cloud.cxx \
@@ -20,6 +23,9 @@ libsgsky_a_SOURCES = \
oursun.cxx \
sky.cxx \
sphere.cxx \
stars.cxx
stars.cxx \
bbcache.cxx \
cloudfield.cxx \
newcloud.cxx
INCLUDES = -I$(top_srcdir)

View File

@@ -0,0 +1,339 @@
// Billboard helper class
//
// Written by Harald JOHNSEN, started April 2005.
//
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
//
//
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include <simgear/compiler.h>
#include <plib/sg.h>
#include <plib/ssg.h>
#include <simgear/screen/extensions.hxx>
#include <simgear/screen/RenderTexture.h>
#include SG_GLUT_H
#include "bbcache.hxx"
/*
memory usage :
size 1 tex 8 tex 32 tex 64 tex
64x64x4 16k 128k 512k 1Mo
128x128x4 64k 512k 2Mo 4Mo
256x256x4 256k 2Mo 8Mo 16Mo
*/
void SGBbCache::freeTextureMemory(void) {
if( bbListCount ) {
for(int i = 0 ; i < bbListCount ; i++) {
if(bbList[i].texID)
glDeleteTextures(1, & bbList[i].texID);
}
delete [] bbList;
}
bbListCount = 0;
cacheSizeKb = 0;
textureWH = 0;
}
bool SGBbCache::allocTextureMemory(int cacheCount, int textureDimension) {
textureWH = textureDimension;
bbListCount = cacheCount;
bbList = new bbInfo[bbListCount];
for(int i = 0 ; i < bbListCount ; i++) {
bbList[i].cldID = 0;
bbList[i].texID = 0;
glGenTextures(1, &bbList[i].texID);
glBindTexture(GL_TEXTURE_2D, bbList[i].texID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8,
textureDimension, textureDimension, 0, GL_RGB, GL_FLOAT, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
glBindTexture(GL_TEXTURE_2D, 0);
cacheSizeKb = (textureDimension * textureDimension * 4);
cacheSizeKb *= cacheCount;
cacheSizeKb /= 1024;
if(rt) {
rt->BeginCapture();
glViewport(0, 0, textureDimension, textureDimension);
rt->EndCapture();
}
return true;
}
SGBbCache::SGBbCache(void) :
bbListCount(0),
cacheSizeKb(0),
textureWH(0),
builtBBCount(0),
rt(0),
rtAvailable(false),
frameNumber(0),
maxImpostorRegenFrame(10)
{
}
SGBbCache::~SGBbCache(void) {
if(rt)
delete rt;
freeTextureMemory();
}
void SGBbCache::init(int cacheCount) {
rt = new RenderTexture();
// don't use default rtt on nvidia/win because of poor performance of glCopyTexSubImage2D
// wihtout default pattrib params - see opengl forum
rt->Reset("rgba tex2D ctt");
// rt->Reset("rgba tex2D");
if( rt->Initialize(256, 256, true) ) {
rtAvailable = true;
if (rt->BeginCapture())
{
glViewport(0, 0, 256, 256);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0, 1, 1, 5.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glDisable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
glDisable(GL_CULL_FACE);
glDisable(GL_FOG);
glDisable(GL_DEPTH_TEST);
glClearColor(0.0, 0.0, 0.0, 0.0);
glEnable(GL_TEXTURE_2D);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.0f);
glEnable(GL_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA );
rt->EndCapture();
}
}
if( cacheCount )
allocTextureMemory( cacheCount, 256 );
}
// TODO:not callable atm, texture size not handled correctly
bool SGBbCache::setCacheSize(int count, int textureDimension) {
if( count < 0 || count > 500)
return false;
freeTextureMemory();
if( count == 0)
return true;
// only allow some reasonable dimensions
switch(textureDimension) {
case 0:
// default size
textureDimension = 256;
break;
case 64:
case 128:
case 256:
break;
case 512:
// rt is 256 so higher texture size has no meaning
textureDimension = 256;
break;
default:
textureDimension = 128;
break;
}
return allocTextureMemory( count, textureDimension);
}
// TODO:not callable atm, texture size not handled correctly
bool SGBbCache::setCacheSize(int sizeKb) {
if( sizeKb < 0 || sizeKb > 256*1024)
return false;
freeTextureMemory();
if( sizeKb == 0)
return true;
int count = 1;
int textureDimension = 256;
if( cacheSizeKb >= 8*1024 ) {
// more than 32 256x256 textures
textureDimension = 256;
} else if( cacheSizeKb >= 2*1024 ) {
// more than 32 128x128 textures
textureDimension = 128;
} else {
// don't go under 64x64 textures
textureDimension = 64;
}
count = (sizeKb * 1024) / (textureDimension * textureDimension * 4);
if(count == 0)
count = 1;
return allocTextureMemory( count, textureDimension);
}
int SGBbCache::queryCacheSize(void) {
return cacheSizeKb;
}
void SGBbCache::free(int bbId, int cldId) {
if( bbId < 0 || bbId >= bbListCount )
return;
if( bbList[bbId].cldID != cldId )
return;
bbList[bbId].cldID = 0;
}
int SGBbCache::alloc(int cldId) {
// pretend we have no more texture if render to texture is not available
if( ! rtAvailable )
return -1;
for(int i = 0 ; i < bbListCount ; i++) {
if( (bbList[i].cldID == 0) && (bbList[i].texID != 0) ) {
bbList[i].cldID = cldId;
bbList[i].angleX = -999;
bbList[i].angleY = -999;
bbList[i].frameUsed = 0;
return i;
}
}
return -1;
}
GLuint SGBbCache::QueryTexID(int cldId, int bbId) {
if( bbId < 0 || bbId >= bbListCount )
return 0;
if( bbList[bbId].cldID != cldId )
return 0;
return bbList[bbId].texID;
}
int SGBbCache::queryImpostorAge(int bbId) {
if( bbId < 0 || bbId >= bbListCount )
return 0;
return frameNumber - bbList[bbId].frame;
}
void SGBbCache::beginCapture(void) {
rt->BeginCapture();
glClear(GL_COLOR_BUFFER_BIT);
}
void SGBbCache::setRadius(float radius, float dist_center) {
float border;
//set viewport to texture resolution
//glViewport(0, 0, 256, 256);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float near_ = dist_center - radius;
float far_ = dist_center + radius;
if( near_ <= 0 ) {
// we are in trouble
glFrustum(-1, 1, -1, 1, 1, 1 + radius * 2);
} else {
border = (near_ * radius) / sqrt(dist_center * dist_center - radius * radius);
glFrustum(-border, border, -border, border, near_, far_);
}
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void SGBbCache::setTextureData(int bbId) {
if( bbId < 0 || bbId >= bbListCount )
return;
glBindTexture(GL_TEXTURE_2D, bbList[bbId].texID);
glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, textureWH, textureWH);
// bbList[bbId].angleX = angleX;
// bbList[bbId].angleY = angleY;
bbList[bbId].frame = frameNumber;
bbList[bbId].frameUsed = frameNumber;
builtBBCount ++;
builtBBframe ++;
}
void SGBbCache::endCapture(void) {
rt->EndCapture();
// glBindTexture(GL_TEXTURE_2D, rt->GetTextureID() );
}
bool SGBbCache::isBbValid( int cldId, int bbId, float angleY, float angleX) {
if( bbId < 0 || bbId >= bbListCount )
return false;
if( bbList[bbId].cldID != cldId )
return false;
// it was just allocated
if( bbList[bbId].frameUsed == 0)
return false;
// we reuse old impostor to speed up things
if( builtBBframe >= maxImpostorRegenFrame )
return true;
if( fabs(angleY - bbList[bbId].angleY) >= 5.0 )
return false;
if( fabs(angleX - bbList[bbId].angleX) >= 5.0 )
return false;
bbList[bbId].frameUsed = frameNumber;
return true;
}
// TODO:this is not the right way to handle that
void SGBbCache::setReference( int cldId, int bbId, float angleY, float angleX) {
if( bbId < 0 || bbId >= bbListCount )
return;
if( bbList[bbId].cldID != cldId )
return;
bbList[bbId].angleX = angleX;
bbList[bbId].angleY = angleY;
}
void SGBbCache::startNewFrame(void) {
builtBBframe = 0;
// TOTO:find reasonable value
int minFrameNumber = frameNumber - 500;
frameNumber++;
// cleanup of unused enties
for( int bbId = 0 ; bbId < bbListCount ; bbId++)
if( (bbList[bbId].cldID != 0) && (bbList[bbId].frameUsed < minFrameNumber) ) {
// entry is now free
bbList[bbId].cldID = 0;
}
}

View File

@@ -0,0 +1,117 @@
// Billboard helper class
//
// Written by Harald JOHNSEN, started April 2005.
//
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
//
//
#ifndef _BBCACHE_HXX
#define _BBCACHE_HXX
#include <plib/sg.h>
#include <plib/ssg.h>
#include <simgear/screen/extensions.hxx>
#include <simgear/screen/RenderTexture.h>
class SGBbCache {
private:
typedef struct {
GLuint texID;
int cldID;
float angleX, angleY;
// creation frame number for debug only
int frame;
// last time this entry was used
int frameUsed;
} bbInfo;
void freeTextureMemory(void);
bool allocTextureMemory(int count, int textureDimension);
// a list of impostors
bbInfo *bbList;
int bbListCount;
int textureWH;
int cacheSizeKb;
// for debug only, stats
int builtBBCount;
// count of generated BB during the current frame
int builtBBframe;
long frameNumber;
RenderTexture *rt;
bool rtAvailable;
public:
SGBbCache(void);
~SGBbCache(void);
// call this first to initialize everything, cacheCount is the number of texture to allocate
void init(int cacheCount);
// free one cache slot, usualy when the cached object is destroyed
void free(int bbId, int cldId);
// allocate a new texture, return an index in the cache
int alloc(int cldId);
// give the texture name to use
GLuint QueryTexID(int cldId, int bbId);
// save the rendered texture from the current context to a new texture
void setTextureData(int bbId);
// start the rendering of a billboard in the RTT context
void beginCapture(void);
// adjust the projection matrix of the RTT context to the size of the object
void setRadius(float radius, float dist_center);
// forget the RTT and go back to the previous rendering context
void endCapture(void);
// for debugging only, give the number of frames since the inpostor was built
int queryImpostorAge(int bbId);
// can we still use this impostor ?
bool isBbValid( int cloudId, int bbId, float angleY, float angleX);
// save view angles of this billboard
void setReference( int cloudId, int bbId, float angleY, float angleX);
// prepare the cache for the rendering of a new frame
void startNewFrame(void);
// alloc the impostors texture memory given the size of the memory pool
// if sizeKb == 0 then the memory pool is freed and impostors are disabled
bool setCacheSize(int sizeKb);
// alloc the impostors texture memory given the count and size of texture
// if count == 0 then the memory pool is freed and impostors are disabled
bool setCacheSize(int count, int textureDimension);
// return the size of the memory pool used by texture impostors
int queryCacheSize(void);
int maxImpostorRegenFrame;
};
#endif // _BBCACHE_HXX

View File

@@ -39,6 +39,8 @@ inline int (isnan)(double r) { return isnan(r); }
#include <simgear/screen/extensions.hxx>
#include <simgear/screen/texture.hxx>
#include "newcloud.hxx"
#include "cloudfield.hxx"
#include "cloud.hxx"
#if defined(__MINGW32__)
@@ -221,12 +223,14 @@ SGCloudLayer::SGCloudLayer( const string &tex_path ) :
layer[0] = layer[1] = layer[2] = layer[3] = NULL;
layer_root->addKid(layer_transform);
layer3D = new SGCloudField;
rebuild();
}
// Destructor
SGCloudLayer::~SGCloudLayer()
{
delete layer3D;
delete vertices;
delete indices;
delete layer_root; // deletes layer_transform and layer as well
@@ -460,6 +464,8 @@ SGCloudLayer::rebuild()
layer_states[SG_CLOUD_CLEAR] = 0;
}
SGNewCloud::loadTextures(texture_path.str());
layer3D->buildTestLayer();
}
if ( bump_mapping ) {
@@ -837,6 +843,7 @@ bool SGCloudLayer::reposition( sgVec3 p, sgVec3 up, double lon, double lat,
last_lat = lat;
}
layer3D->reposition( p, up, lon, lat, alt, dt);
return true;
}
@@ -844,6 +851,9 @@ bool SGCloudLayer::reposition( sgVec3 p, sgVec3 up, double lon, double lat,
void SGCloudLayer::draw( bool top ) {
if ( layer_coverage != SG_CLOUD_CLEAR ) {
if ( SGCloudField::enable3D )
layer3D->Render();
else
if ( bump_mapping && enable_bump_mapping ) {
sgMat4 modelview,

View File

@@ -39,6 +39,7 @@ SG_USING_STD(string);
// SG_USING_STD(cout);
// SG_USING_STD(endl);
class SGCloudField;
/**
* A class layer to model a single cloud layer
@@ -225,6 +226,8 @@ private:
// position, not view position
// double xoff, yoff;
double last_lon, last_lat, last_course;
SGCloudField *layer3D;
};

View File

@@ -0,0 +1,327 @@
// a layer of 3d clouds
//
// Written by Harald JOHNSEN, started April 2005.
//
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
//
//
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include <simgear/compiler.h>
#include <plib/sg.h>
#include <plib/ssg.h>
#include <simgear/math/sg_geodesy.hxx>
#include STL_ALGORITHM
#include SG_GLUT_H
#include <vector>
SG_USING_STD(vector);
#include "newcloud.hxx"
#include "cloudfield.hxx"
static list_of_culledCloud inViewClouds;
// visibility distance for clouds in meters
float SGCloudField::CloudVis = 25000.0f;
bool SGCloudField::enable3D = true;
// fieldSize must be > CloudVis or we can destroy the impostor cache
// a cloud must only be seen once or the impostor will be generated for each of his positions
double SGCloudField::fieldSize = 30000.0;
float SGCloudField::density = 100.0;
static int last_cache_size = 4*1024;
int SGCloudField::get_CacheSize(void) {
return SGNewCloud::cldCache->queryCacheSize();
}
void SGCloudField::set_CacheSize(int sizeKb) {
// apply in rendering option dialog
if(last_cache_size == sizeKb)
return;
if(sizeKb == 0)
return;
if(sizeKb)
last_cache_size = sizeKb;
SGNewCloud::cldCache->setCacheSize(sizeKb);
}
void SGCloudField::set_CloudVis(float distance) {
SGCloudField::CloudVis = distance;
}
void SGCloudField::set_density(float density) {
SGCloudField::density = density;
}
void SGCloudField::set_enable3dClouds(bool enable) {
if(enable3D == enable)
return;
enable3D = enable;
if(enable) {
SGNewCloud::cldCache->setCacheSize(last_cache_size);
} else {
SGNewCloud::cldCache->setCacheSize(0);
}
}
// reposition the cloud layer at the specified origin and orientation
void SGCloudField::reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt) {
sgMat4 T1, LON, LAT;
sgVec3 axis;
sgMakeTransMat4( T1, p );
sgSetVec3( axis, 0.0, 0.0, 1.0 );
sgMakeRotMat4( LON, lon * SGD_RADIANS_TO_DEGREES, axis );
sgSetVec3( axis, 0.0, 1.0, 0.0 );
sgMakeRotMat4( LAT, 90.0 - lat * SGD_RADIANS_TO_DEGREES, axis );
sgMat4 TRANSFORM;
sgCopyMat4( TRANSFORM, T1 );
sgPreMultMat4( TRANSFORM, LON );
sgPreMultMat4( TRANSFORM, LAT );
sgCoord layerpos;
sgSetCoord( &layerpos, TRANSFORM );
sgMakeCoordMat4( transform, &layerpos );
// TODO:use a simple sphere earth
double az1, az2, s;
geo_inverse_wgs_84( 0.0, 0.0, 0.0, lat*SG_RADIANS_TO_DEGREES , lon*SG_RADIANS_TO_DEGREES, &az1, &az2, &s);
az1 = az1 * SG_DEGREES_TO_RADIANS;
// compute the view position on a 'flat' earth
deltay = -cos(SG_PI/2+az1) * s;
deltax = -sin(SG_PI/2+az1) * s;
// deltax = cos(0.0) * s;
// deltay = sin(0.0) * s;
this->alt = alt;
// simulate clouds movement from wind
double speed = 10.0f;
double direction = 45.0;
double sp_dist = speed*dt;
if (sp_dist > 0) {
double bx = cos((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
double by = sin((180.0-direction) * SGD_DEGREES_TO_RADIANS) * sp_dist;
relative_position[SG_X] += bx;
relative_position[SG_Y] += by;
}
// correct the frustum with the right far plane
ssgContext *context = ssgGetCurrentContext();
frustum = *context->getFrustum();
frustum.setNearFar(1.0, CloudVis);
}
SGCloudField::SGCloudField() {
sgSetVec3( relative_position, 0,0,0);
theField.reserve(200);
inViewClouds.reserve(200);
}
SGCloudField::~SGCloudField() {
list_of_Cloud::iterator iCloud;
for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
delete iCloud->aCloud;
}
theField.clear();
}
// add one cloud, data is not copied, ownership given
void SGCloudField::addCloud( sgVec3 pos, SGNewCloud *cloud) {
Cloud cl;
sgCopyVec3( cl.pos, pos );
cl.aCloud = cloud;
cloud->SetPos( pos );
theField.push_back( cl );
}
static float Rnd(float n) {
return n * (-0.5f + rand() / (float) RAND_MAX);
}
// for debug only
// build a field of cloud of size 25x25 km, its a grid of 11x11 clouds
void SGCloudField::buildTestLayer(void) {
const float s = 2200.0f;
for( int z = -5 ; z <= 5 ; z++) {
for( int x = -5 ; x <= 5 ; x++ ) {
SGNewCloud *cloud = new SGNewCloud;
cloud->new_cu();
sgVec3 pos = {(x+Rnd(0.7)) * s, 750.0f, (z+Rnd(0.7)) * s};
addCloud(pos, cloud);
}
}
}
// cull all clouds of a tiled field
void SGCloudField::cullClouds(sgVec3 eyePos, sgMat4 mat) {
list_of_Cloud::iterator iCloud;
// const float distVisCompare = CloudVis * CloudVis;
// TODO:cull the field before culling the clouds in the field (should eliminate 3 fields)
for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) {
sgVec3 dist;
sgSphere sphere;
sgSubVec3( dist, iCloud->pos, eyePos );
sphere.setCenter(dist[0], dist[2], dist[1]);
sphere.setRadius(iCloud->aCloud->getRadius());
sphere.orthoXform(mat);
if( frustum.contains( & sphere ) != SG_OUTSIDE ) {
float squareDist = dist[0]*dist[0] + dist[1]*dist[1] + dist[2]*dist[2];
culledCloud tmp;
tmp.aCloud = iCloud->aCloud;
sgCopyVec3( tmp.eyePos, eyePos );
// save distance for later sort, opposite distance because we want back to front
tmp.dist = - squareDist;
inViewClouds.push_back(tmp);
}
}
}
static inline void myswap(float &a, float &b) {
float tmp = a;
a = b;
b = tmp;
}
// Render a cloud field
// because no field can have an infinite size (and we don't want to reach his border)
// we draw this field and adjacent fields.
// adjacent fields are not real, its the same field displaced by some offset
void SGCloudField::Render(void) {
sgVec3 eyePos;
double relx, rely;
if( ! enable3D )
return;
// ask the impostor cache to do some cleanup
// TODO:don't do that for every field
SGNewCloud::cldCache->startNewFrame();
inViewClouds.clear();
// cloud fields are tiled on the flat earth
// compute the position in the tile
relx = -fmod( deltax + relative_position[SG_X], fieldSize );
rely = -fmod( deltay + relative_position[SG_Y], fieldSize );
glPushMatrix();
sgMat4 modelview, tmp, invtrans;
// try to find the sun position (buggy)
sgTransposeNegateMat4( invtrans, transform );
sgVec3 lightVec;
ssgGetLight( 0 )->getPosition( lightVec );
sgNegateVec3( lightVec );
sgXformVec3( lightVec, invtrans );
sgNormaliseVec3( lightVec );
sgCopyVec3( SGNewCloud::modelSunDir, lightVec );
// try to find the lighting data (buggy)
sgVec4 diffuse, ambient;
ssgGetLight( 0 )->getColour( GL_DIFFUSE, diffuse );
ssgGetLight( 0 )->getColour( GL_AMBIENT, ambient );
sgScaleVec3 ( SGNewCloud::sunlight, diffuse , 0.70f);
sgScaleVec3 ( SGNewCloud::ambLight, ambient , 0.60f);
// voodoo things on the matrix stack
ssgGetModelviewMatrix( modelview );
sgCopyMat4( tmp, transform );
sgPostMultMat4( tmp, modelview );
sgSetVec3( eyePos, -relx, -tmp[3][2], -rely);
sgSetVec3( eyePos, -relx, 0, -rely);
sgSetVec3( eyePos, -relx, alt, -rely);
// sgSetVec3( eyePos, 0, - tmp[3][2], 0);
// sgSetVec3( eyePos, 20000, - tmp[3][2], 20000);
tmp[3][2] = 0;
tmp[3][0] = 0;
tmp[3][1] = 0;
ssgLoadModelviewMatrix( tmp );
/* flat earth
^
|
| FFF
| FoF
| FFF
|
O----------->
o = we are here
F = adjacent fields
*/
for(int x = -1 ; x <= 1 ; x++)
for(int y = -1 ; y <= 1 ; y++ ) {
sgVec3 fieldPos;
// pretend we are not where we are
sgSetVec3(fieldPos, eyePos[0] + x*fieldSize, eyePos[1], eyePos[2] + y*fieldSize);
cullClouds(fieldPos, tmp);
}
// sort all visible clouds back to front (because of transparency)
sort( inViewClouds.begin(), inViewClouds.end() );
// TODO:push states
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.0f);
glDisable(GL_CULL_FACE);
// glDisable(GL_DEPTH_TEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_SMOOTH);
glEnable(GL_BLEND);
glBlendFunc( GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA );
// glEnable( GL_COLOR_MATERIAL );
glEnable( GL_TEXTURE_2D );
glDisable( GL_FOG );
glDisable(GL_LIGHTING);
// test data: field = 11x11 cloud, 9 fields
// depending on position and view direction, perhaps 40 to 60 clouds not culled
list_of_culledCloud::iterator iCloud;
for( iCloud = inViewClouds.begin() ; iCloud != inViewClouds.end() ; iCloud++ ) {
// iCloud->aCloud->drawContainers();
iCloud->aCloud->Render(iCloud->eyePos);
}
glBindTexture(GL_TEXTURE_2D, 0);
glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ;
ssgLoadModelviewMatrix( modelview );
glPopMatrix();
// glEnable(GL_DEPTH_TEST);
}

View File

@@ -0,0 +1,114 @@
// a layer of 3d clouds
//
// Written by Harald JOHNSEN, started April 2005.
//
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
//
//
#ifndef _CLOUDFIELD_HXX
#define _CLOUDFIELD_HXX
#include <plib/sg.h>
#include <simgear/compiler.h>
#include <vector>
SG_USING_STD(vector);
class SGNewCloud;
class culledCloud {
public:
SGNewCloud *aCloud;
sgVec3 eyePos;
float dist;
bool operator<(const culledCloud &b) const {
return (this->dist < b.dist);
}
};
typedef vector<culledCloud> list_of_culledCloud;
class SGCloudField {
private:
class Cloud {
public:
SGNewCloud *aCloud;
sgVec3 pos;
// float dist;
// bool culled;
// bool operator<(const Cloud &b) {
// return this->dist < b.dist;
// }
};
typedef vector<Cloud> list_of_Cloud;
// cull all clouds of a tiled field
void cullClouds(sgVec3 eyePos, sgMat4 mat);
list_of_Cloud theField;
// this is a relative position only, with that we can move all clouds at once
sgVec3 relative_position;
// double lon, lat;
sgFrustum frustum;
sgMat4 transform;
double deltax, deltay, alt;
public:
SGCloudField();
~SGCloudField();
// add one cloud, data is not copied, ownership given
void addCloud( sgVec3 pos, SGNewCloud *cloud);
// for debug only
void buildTestLayer(void);
// Render a cloud field
void Render(void);
// reposition the cloud layer at the specified origin and orientation
void reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt);
// visibility distance for clouds in meters
static float CloudVis;
static float density;
static double fieldSize;
static bool enable3D;
// return the size of the memory pool used by texture impostors
static int get_CacheSize(void);
static float get_CloudVis(void) { return CloudVis; }
static float get_density(void) { return density; }
static bool get_enable3dClouds(void) { return enable3D; }
static void set_CacheSize(int sizeKb);
static void set_CloudVis(float distance);
static void set_density(float density);
static void set_enable3dClouds(bool enable);
};
#endif // _CLOUDFIELD_HXX

View File

@@ -0,0 +1,726 @@
// 3D cloud class
//
// Written by Harald JOHNSEN, started April 2005.
//
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
//
//
#ifdef HAVE_CONFIG_H
# include <simgear_config.h>
#endif
#include <simgear/compiler.h>
#include <plib/sg.h>
#include <plib/ssg.h>
#include <simgear/misc/sg_path.hxx>
#include STL_ALGORITHM
#include SG_GLUT_H
#include "newcloud.hxx"
/*
*/
static ssgTexture *cloudTextures[SGNewCloud::CLTexture_max];
bool SGNewCloud::useAnisotropic = true;
SGBbCache *SGNewCloud::cldCache = 0;
static bool texturesLoaded = false;
float SGNewCloud::nearRadius = 2500.0f;
bool SGNewCloud::lowQuality = false;
sgVec3 SGNewCloud::sunlight = {0.5f, 0.5f, 0.5f};
sgVec3 SGNewCloud::ambLight = {0.5f, 0.5f, 0.5f};
sgVec3 SGNewCloud::modelSunDir = {0,1,0};
// constructor
SGNewCloud::SGNewCloud() :
bbId(-1),
// rank(-1),
minx(999), miny(999), minz(999), maxx(-999), maxy(-999), maxz(-999)
{
cloudId = (int) this;
sgSetVec3(center, 0.0f, 0.0f, 0.0f);
sgSetVec3(cloudpos, 0.0f, 0.0f, 0.0f);
list_spriteContainer.reserve(8);
list_spriteDef.reserve(40);
// if( ! texturesLoaded ) {}
if( cldCache == 0 ) {
cldCache = new SGBbCache;
cldCache->init( 20 );
}
}
SGNewCloud::~SGNewCloud() {
list_spriteDef.clear();
list_spriteContainer.clear();
cldCache->free( bbId, cloudId );
}
// load all textures used to draw cloud sprites
void SGNewCloud::loadTextures(const string &tex_path) {
if( texturesLoaded )
return;
texturesLoaded = true;
SGPath cloud_path;
cloud_path.set(tex_path);
cloud_path.append("cl_cumulus.rgb");
cloudTextures[ CLTexture_cumulus ] = new ssgTexture( cloud_path.str().c_str(), false, false, false );
cloudTextures[ CLTexture_cumulus ]->ref();
cloud_path.set(tex_path);
cloud_path.append("cl_stratus.rgb");
cloudTextures[ CLTexture_stratus ] = new ssgTexture( cloud_path.str().c_str(), false, false, false );
cloudTextures[ CLTexture_stratus ]->ref();
}
void SGNewCloud::startFade(bool direction, float duration, float pauseLength) {
}
void SGNewCloud::setFade(float howMuch) {
}
static float rayleighCoeffAngular(float cosAngle) {
return 3.0f / 4.0f * (1.0f + cosAngle * cosAngle);
}
// cp is normalized (len==1)
static void CartToPolar3d(sgVec3 cp, sgVec3 polar) {
polar[0] = atan2(cp[1], cp[0]);
polar[1] = SG_PI / 2.0f - atan2(sqrtf (cp[0] * cp[0] + cp[1] * cp[1]), cp[2]);
polar[2] = 1.0f;
}
static void PolarToCart3d(sgVec3 p, sgVec3 cart) {
float tmp = cos(p[1]);
cart[0] = cos(p[0]) * tmp;
cart[1] = sin(p[0]) * tmp;
cart[2] = sin(p[1]);
}
// compute the light for a cloud sprite corner
// from the normal and the sun, scaled by the Rayleigh factor
// and finaly added to the ambient light
static void lightFunction(sgVec3 normal, sgVec4 light, float pf) {
float cosAngle = sgScalarProductVec3( normal, SGNewCloud::modelSunDir);
float vl = (1.0f - 0.1f + cosAngle / 10.0f) * pf;
sgScaleVec3( light, SGNewCloud::sunlight, vl );
sgAddVec3( light, SGNewCloud::ambLight );
// we need to clamp or else the light will bug when adding transparency
if( light[0] > 1.0 ) light[0] = 1.0;
if( light[1] > 1.0 ) light[1] = 1.0;
if( light[2] > 1.0 ) light[2] = 1.0;
light[3] = 1.0;
}
// compute the light for a cloud sprite
// we use ambient light and orientation versus sun position
// TODO:check sun pos and check code
void SGNewCloud::computeSimpleLight(sgVec3 FakeEyePos) {
// constant Rayleigh factor if we are not doing Anisotropic lighting
float pf = 1.0f;
const float ang = 45.0f * SG_PI / 180.0f;
list_of_spriteDef::iterator iSprite;
for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
if( useAnisotropic ) {
sgVec3 eyeDir;
sgSubVec3(eyeDir, iSprite->pos, FakeEyePos);
sgNormaliseVec3(eyeDir);
float cosAngle = sgScalarProductVec3(eyeDir, modelSunDir);
pf = rayleighCoeffAngular(cosAngle);
}
// compute the vector going from the container box center to the sprite
// TODO : this is a constant except for cloudpos, compute the normal in setpos function
sgVec3 normal;
spriteContainer *thisSpriteContainer = &list_spriteContainer[iSprite->box];
sgSubVec3(normal, iSprite->pos, thisSpriteContainer->pos);
sgSubVec3(normal, thisSpriteContainer->center);
sgSubVec3(normal, cloudpos);
sgNormaliseVec3(normal);
if( lowQuality ) {
// juste use the traditional normal to compute some lightning
sgVec4 centerColor;
lightFunction(normal, centerColor, pf);
sgCopyVec4(iSprite->l0, centerColor);
sgCopyVec4(iSprite->l1, centerColor);
sgCopyVec4(iSprite->l2, centerColor);
sgCopyVec4(iSprite->l3, centerColor);
} else {
// use exotic lightning function, this will give more 'relief' to the clouds
// compute a normal for each vextex this will simulate a smooth shading for a round shape
sgVec3 polar, cart, pt;
// I suspect this code to be bugged...
CartToPolar3d(normal, polar);
// offset the normal vector by some angle for each vertex
sgSetVec3(pt, polar[0] - ang, polar[1] - ang, polar[2]);
PolarToCart3d(pt, cart);
lightFunction(cart, iSprite->l0, pf);
sgSetVec3(pt, polar[0] + ang, polar[1] - ang, polar[2]);
PolarToCart3d(pt, cart);
lightFunction(cart, iSprite->l1, pf);
sgSetVec3(pt, polar[0] + ang, polar[1] + ang, polar[2]);
PolarToCart3d(pt, cart);
lightFunction(cart, iSprite->l2, pf);
sgSetVec3(pt, polar[0] - ang, polar[1] + ang, polar[2]);
PolarToCart3d(pt, cart);
lightFunction(cart, iSprite->l3, pf);
}
}
}
// add a new box to the cloud
void SGNewCloud::addContainer (float x, float y, float z, float r, CLbox_type type) {
spriteContainer cont;
sgSetVec3( cont.pos, x, y, z );
cont.r = r;
cont.cont_type = type;
sgSetVec3( cont.center, 0.0f, 0.0f, 0.0f);
list_spriteContainer.push_back( cont );
}
// add a sprite inside a box
void SGNewCloud::addSprite(float x, float y, float z, float r, CLbox_type type, int box) {
spriteDef newSpriteDef;
int rank = list_spriteDef.size();
sgSetVec3( newSpriteDef.pos, x, y, z);
newSpriteDef.box = box;
newSpriteDef.sprite_type = type;
newSpriteDef.rank = rank;
newSpriteDef.r = r;
list_spriteDef.push_back( newSpriteDef );
spriteContainer *thisBox = &list_spriteContainer[box];
sgVec3 deltaPos;
sgSubVec3( deltaPos, newSpriteDef.pos, thisBox->pos );
sgAddVec3( thisBox->center, deltaPos );
r = r * 0.6f; // 0.5 * 1.xxx
if( x - r < minx )
minx = x - r;
if( y - r < miny )
miny = y - r;
if( z - r < minz )
minz = z - r;
if( x + r > maxx )
maxx = x + r;
if( y + r > maxy )
maxy = y + r;
if( z + r > maxz )
maxz = z + r;
}
// return a random number between -n/2 and n/2
static float Rnd(float n) {
return n * (-0.5f + rand() / (float) RAND_MAX);
}
// generate all sprite with defined boxes
void SGNewCloud::genSprites( void ) {
float x, y, z, r;
int N, sc;
N = list_spriteContainer.size();
for(int i = 0 ; i < N ; i++ ) {
spriteContainer *thisBox = & list_spriteContainer[i];
// the type defines how the sprites can be positioned inside the box, their size, etc
switch(thisBox->cont_type) {
case CLbox_sc:
for( sc = 0 ; sc <= 4 ; sc ++ ) {
r = thisBox->r + Rnd(0.2f);
x = thisBox->pos[SG_X] + Rnd(thisBox->r);
y = thisBox->pos[SG_Y] + Rnd(thisBox->r * 0.2f);
z = thisBox->pos[SG_Z] + Rnd(thisBox->r);
addSprite(x, y, z, r, thisBox->cont_type, i);
}
break;
case CLbox_stratus:
sc = 1;
r = thisBox->r;
x = thisBox->pos[SG_X];
y = thisBox->pos[SG_Y];
z = thisBox->pos[SG_Z];
addSprite(x, y, z, r, thisBox->cont_type, i);
break;
case CLbox_cumulus:
for( sc = 0 ; sc <= 4 ; sc ++ ) {
r = thisBox->r + Rnd(0.2f);
x = thisBox->pos[SG_X] + Rnd(thisBox->r * 0.75f);
y = thisBox->pos[SG_Y] + Rnd(thisBox->r * 0.5f);
z = thisBox->pos[SG_Z] + Rnd(thisBox->r * 0.75f);
if ( y < thisBox->pos[SG_Y] - thisBox->r / 10.0f )
y = thisBox->pos[SG_Y] - thisBox->r / 10.0f;
addSprite(x, y, z, r, thisBox->cont_type, i);
}
break;
default:
for( sc = 0 ; sc <= 4 ; sc ++ ) {
r = thisBox->r + Rnd(0.2f);
x = thisBox->pos[SG_X] + Rnd(thisBox->r);
y = thisBox->pos[SG_Y] + Rnd(thisBox->r);
z = thisBox->pos[SG_Z] + Rnd(thisBox->r);
addSprite(x, y, z, r, thisBox->cont_type, i);
}
break;
}
sgScaleVec3(thisBox->center, 1.0f / sc);
}
radius = maxx - minx;
if ( (maxy - miny) > radius )
radius = (maxy - miny);
if ( (maxz - minz) > radius )
radius = (maxz - minz);
radius /= 2.0f;
sgSetVec3( center, (maxx + minx) / 2.0f, (maxy + miny) / 2.0f, (maxz + minz) / 2.0f );
/* fadingrank = 0
' fadingrank = UBound(tbSpriteDef()) * 10
fadingdir = 0*/
// TODO : compute initial sprite normals for lighting function
}
// definition of a cu cloud, only for testing
void SGNewCloud::new_cu(void) {
float s = 150.0f;
float r = Rnd(1.0) + 0.5;
if( r < 0.5f ) {
addContainer(0.0f, 0.0f, 0.0f, s, CLbox_cumulus);
addContainer(s, 0, 0, s, CLbox_cumulus);
addContainer(0, 0, 2 * s, s, CLbox_cumulus);
addContainer(s, 0, 2 * s, s, CLbox_cumulus);
addContainer(-1.2f * s, 0.2f * s, s, s * 1.4f, CLbox_cumulus);
addContainer(0.2f * s, 0.2f * s, s, s * 1.4f, CLbox_cumulus);
addContainer(1.6f * s, 0.2f * s, s, s * 1.4f, CLbox_cumulus);
} else if ( r < 0.90f ) {
addContainer(0, 0, 0, s * 1.2, CLbox_cumulus);
addContainer(s, 0, 0, s, CLbox_cumulus);
addContainer(0, 0, s, s, CLbox_cumulus);
addContainer(s * 1.1, 0, s, s * 1.2, CLbox_cumulus);
addContainer(-1.2 * s, 1 + 0.2 * s, s * 0.5, s * 1.4, CLbox_standard);
addContainer(0.2 * s, 1 + 0.25 * s, s * 0.5, s * 1.5, CLbox_standard);
addContainer(1.6 * s, 1 + 0.2 * s, s * 0.5, s * 1.4, CLbox_standard);
} else {
// cb
s = 475.0f;
addContainer(0, 0, 0, s, CLbox_cumulus);
addContainer(0, 0, s, s, CLbox_cumulus);
addContainer(s, 0, s, s, CLbox_cumulus);
addContainer(s, 0, 0, s, CLbox_cumulus);
addContainer(s / 2, s, s / 2, s * 1.5, CLbox_standard);
addContainer(0, 2 * s, 0, s, CLbox_standard);
addContainer(0, 2 * s, s, s, CLbox_standard);
addContainer(s, 2 * s, s, s, CLbox_standard);
addContainer(s, 2 * s, 0, s, CLbox_standard);
}
genSprites();
}
// define the new position of the cloud (inside the cloud field, not on sphere)
void SGNewCloud::SetPos(sgVec3 newPos) {
int N = list_spriteDef.size();
sgVec3 deltaPos;
sgSubVec3( deltaPos, newPos, cloudpos );
// for each particle
for(int i = 0 ; i < N ; i ++) {
sgAddVec3( list_spriteDef[i].pos, deltaPos );
}
sgAddVec3( center, deltaPos );
sgSetVec3( cloudpos, newPos[SG_X], newPos[SG_Y], newPos[SG_Z]);
// TODO : recompute sprite normal so we don't have to redo that each frame
}
/*
Public Sub drawContainers()
Dim N As Integer, i As Integer
N = UBound(tbSpriteCont())
Call glPolygonMode(faceFrontAndBack, pgmLine)
Call glColor3f(0.9, 0.9, 0.9)
For i = 0 To N - 1
With tbSpriteCont(i)
Call glPushMatrix
Call glTranslatef(.x * c_scale + cloudpos(0), .y * c_scale + cloudpos(1), .z * c_scale + cloudpos(2))
Call gCtl.Shapes.box(.r * c_scale, .r * c_scale, .r * c_scale)
Call glPopMatrix
End With
Next i
If 0 Then
Call glPushMatrix
Call glTranslatef(ccenter(0), ccenter(1), ccenter(2))
Call gCtl.Shapes.Sphere(cradius, 8, 8)
Call glPopMatrix
End If
End Sub
*/
void SGNewCloud::drawContainers() {
}
/*
*/
//bool SGNewCloud::compareSpriteFunction(const spriteDef &a, const spriteDef &b) {
// return (a.dist > b.dist);
//}
// sort on distance to eye because of transparency
void SGNewCloud::sortSprite( sgVec3 eye ) {
list_of_spriteDef::iterator iSprite;
// compute distance from sprite to eye
for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
sgVec3 dist;
sgSubVec3( dist, iSprite->pos, eye );
iSprite->dist = -(dist[0]*dist[0] + dist[1]*dist[1] + dist[2]*dist[2]);
}
sort( list_spriteDef.begin(), list_spriteDef.end() );
}
// render the cloud on screen or on the RTT texture to build the impostor
void SGNewCloud::Render3Dcloud( bool drawBB, sgVec3 FakeEyePos, sgVec3 deltaPos, float dist_center ) {
/* int clrank = fadingrank / 10;
int clfadeinrank = fadingrank - clrank * 10;*/
GLint previousTexture = -1, thisTexture;
list_of_spriteDef::iterator iSprite;
for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) {
// choose texture to use depending on sprite type
switch(iSprite->sprite_type) {
case CLbox_stratus:
thisTexture = CLTexture_stratus;
break;
default:
thisTexture = CLTexture_cumulus;
break;
}
// in practice there is no texture switch (atm)
if( previousTexture != thisTexture ) {
previousTexture = thisTexture;
glBindTexture(GL_TEXTURE_2D, cloudTextures[thisTexture]->getHandle());
}
sgVec3 translate;
if( drawBB ) {
sgCopyVec3( translate, iSprite->pos);
sgSubVec3( translate, iSprite->pos, deltaPos );
}
else
sgSubVec3( translate, iSprite->pos, deltaPos);
// flipx and flipy are random texture flip flags, this gives more random clouds
float flipx = (float) ( iSprite->rank & 1 );
float flipy = (float) ( (iSprite->rank >> 1) & 1 );
// cu texture have a flat bottom so we can't do a vertical flip
if( iSprite->sprite_type == CLbox_cumulus || iSprite->sprite_type == CLbox_stratus )
flipy = 0.0f;
if( iSprite->sprite_type == CLbox_stratus )
flipx = 0.0f;
// adjust colors depending on cloud type
// TODO : rewrite that later, still experimental
switch(iSprite->sprite_type) {
case CLbox_cumulus:
// dark bottom
sgScaleVec3(iSprite->l0, 0.6f);
sgScaleVec3(iSprite->l1, 0.6f);
break;
case CLbox_stratus:
// usually dark grey
sgScaleVec3(iSprite->l0, 0.8f);
sgScaleVec3(iSprite->l1, 0.8f);
sgScaleVec3(iSprite->l2, 0.8f);
sgScaleVec3(iSprite->l3, 0.8f);
break;
default:
// darker bottom than top
sgScaleVec3(iSprite->l0, 0.8f);
sgScaleVec3(iSprite->l1, 0.8f);
break;
}
float r = iSprite->r * 0.5f;
sgVec4 l0, l1, l2, l3;
sgCopyVec4 ( l0, iSprite->l0 );
sgCopyVec4 ( l1, iSprite->l1 );
sgCopyVec4 ( l2, iSprite->l2 );
sgCopyVec4 ( l3, iSprite->l3 );
if( ! drawBB ) {
// blend clouds with sky based on distance to limit the contrast of distant cloud
// TODO:use cloudfield vis, not hardcoded value
float t = 1.0f - dist_center / (15000.0f * 2.0 );
if ( t < 0.0f )
t = 0.0f; // no, it should have been culled
// now clouds at the far plane are half blended
sgScaleVec4( l0, t );
sgScaleVec4( l1, t );
sgScaleVec4( l2, t );
sgScaleVec4( l3, t );
}
// compute the rotations so that the quad is facing the camera
sgVec3 pos;
sgSetVec3( pos, translate[SG_X], translate[SG_Z], translate[SG_Y] );
sgCopyVec3( translate, pos );
sgNormaliseVec3( translate );
sgVec3 x, y, up = {0.0f, 0.0f, 1.0f};
sgVectorProductVec3(x, translate, up);
sgNormaliseVec3(x);
sgScaleVec3(x, r);
sgVectorProductVec3(y, x, translate);
sgNormaliseVec3(y);
sgScaleVec3(y, r);
sgVec3 left, right;
if( drawBB )
sgSetVec3( left, iSprite->pos[SG_X], iSprite->pos[SG_Z], iSprite->pos[SG_Y]);
else
sgCopyVec3( left, pos );
sgSubVec3 (left, y);
sgAddVec3 (right, left, x);
sgSubVec3 (left, x);
glBegin(GL_QUADS);
glColor4fv(l0);
glTexCoord2f(flipx, 1.0f - flipy);
glVertex3fv(left);
glColor4fv(l1);
glTexCoord2f(1.0f - flipx, 1.0f - flipy);
glVertex3fv(right);
sgScaleVec3( y, 2.0 );
sgAddVec3( left, y);
sgAddVec3( right, y);
glColor4fv(l2);
glTexCoord2f(1.0f - flipx, flipy);
glVertex3fv(right);
glColor4fv(l3);
glTexCoord2f(flipx, flipy);
glVertex3fv(left);
glEnd();
}
}
// compute rotations so that a quad is facing the camera
// TODO:change obsolete code because we dont use glrotate anymore
void SGNewCloud::CalcAngles(sgVec3 refpos, sgVec3 FakeEyePos, float *angleY, float *angleX) {
sgVec3 upAux, lookAt, objToCamProj, objToCam;
float angle, angle2;
sgSetVec3(objToCamProj, -FakeEyePos[SG_X] + refpos[SG_X], -FakeEyePos[SG_Z] + refpos[SG_Z], 0.0f);
sgNormaliseVec3(objToCamProj);
sgSetVec3(lookAt, 0.0f, 1.0f, 0.0f);
sgVectorProductVec3(upAux, lookAt, objToCamProj);
angle = sgScalarProductVec3(lookAt, objToCamProj);
if( (angle < 0.9999f) && (angle > -0.9999f) ) {
angle = acos(angle) * 180.0f / SG_PI;
if( upAux[2] < 0.0f )
angle = -angle;
} else
angle = 0.0f;
sgSetVec3(objToCam, -FakeEyePos[SG_X] + refpos[SG_X], -FakeEyePos[SG_Z] + refpos[SG_Z], -FakeEyePos[SG_Y] + refpos[SG_Y]);
sgNormaliseVec3(objToCam);
angle2 = sgScalarProductVec3(objToCamProj, objToCam);
if( (angle2 < 0.9999f) && (angle2 > -0.9999f) ) {
angle2 = -acos(angle2) * 180.0f / SG_PI;
if( objToCam[2] > 0.0f )
angle2 = -angle2;
} else
angle2 = 0.0f;
angle2 += 90.0f;
*angleY = angle;
*angleX = angle2;
}
// draw a cloud but this time we use the impostor texture
void SGNewCloud::RenderBB(sgVec3 deltaPos, float angleY, float angleX, float dist_center) {
// TODO:glrotate is not needed
glPushMatrix();
glTranslatef(center[SG_X] - deltaPos[SG_X], center[SG_Z] - deltaPos[SG_Z], center[SG_Y] - deltaPos[SG_Y]);
glRotatef(angleY, 0.0f, 0.0f, 1.0f);
glRotatef(angleX, 1.0f, 0.0f, 0.0f);
// blend clouds with sky based on distance to limit the contrast of distant cloud
// TODO:use cloudfield vis, not hardcoded value
float t = 1.0f - dist_center / (15000.0f * 2.0 );
// err the alpha value is not good for impostor, debug that
t *= 1.65;
if ( t < 0.0f )
t = 0.0f;
glColor4f(t, t, t, t);
float r = radius;
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 1.0f);
glVertex2f(-r, r);
glTexCoord2f(1.0f, 1.0f);
glVertex2f(r, r);
glTexCoord2f(1.0f, 0.0f);
glVertex2f(r, -r);
glTexCoord2f(0.0f, 0.0f);
glVertex2f(-r, -r);
glEnd();
#if 1 // debug only
int age = cldCache->queryImpostorAge(bbId);
// draw a red border for the newly generated BBs else draw a white border
if( age < 200 )
glColor3f(1, 0, 0);
else
glColor3f(1, 1, 1);
glBindTexture(GL_TEXTURE_2D, 0);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBegin(GL_QUADS);
glVertex2f(-r, -r);
glVertex2f(r, -r);
glVertex2f(r, r);
glVertex2f(-r, r);
glEnd();
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
#endif
glPopMatrix();
}
// determine if it is a good idea to use an impostor to render the cloud
bool SGNewCloud::isBillboardable(float dist) {
if( dist <= ( 2.1f * radius ) ) {
// inside cloud
return false;
}
if( (dist-radius) <= nearRadius ) {
// near clouds we don't want to use BB
return false;
}
// return false;
return true;
}
// render the cloud, fakepos is a relative position inside the cloud field
void SGNewCloud::Render(sgVec3 fakepos) {
sgVec3 eyePos, FakeEyePos;
sgVec3 dist;
glColor3f(1.0f, 1.0f, 1.0f);
// obsolete code
sgCopyVec3( eyePos, fakepos );
sgCopyVec3( FakeEyePos, fakepos);
sgVec3 deltaPos;
// sgSubVec3( deltaPos, eyePos, FakeEyePos);
sgCopyVec3( deltaPos, FakeEyePos);
sgSubVec3( dist, center, FakeEyePos);
float dist_center = sgLengthVec3(dist);
// eeeek don't do that so early, perhaps we will use an impostor
computeSimpleLight( FakeEyePos );
// view point sort, we sort because of transparency
// eeeek don't do that so early, perhaps we will use an impostor
sortSprite( FakeEyePos );
if( !isBillboardable(dist_center) ) {
// not a good candidate for impostors, draw a real cloud
Render3Dcloud(false, FakeEyePos, deltaPos, dist_center);
} else {
GLuint texID = 0;
// lets use our impostor
if( bbId >= 0)
texID = cldCache->QueryTexID(cloudId, bbId);
// ok someone took our impostor, so allocate a new one
if( texID == 0 ) {
// allocate a new Impostor
bbId = cldCache->alloc(cloudId);
texID = cldCache->QueryTexID(cloudId, bbId);
}
if( texID == 0 ) {
// no more free texture in the pool
Render3Dcloud(false, FakeEyePos, deltaPos, dist_center);
} else {
float angleX, angleY;
CalcAngles(center, FakeEyePos, &angleY, &angleX);
if( ! cldCache->isBbValid( cloudId, bbId, angleY, angleX) ) {
// we must build or rebuild this billboard
// start render to texture
cldCache->beginCapture();
// set transformation matrices
cldCache->setRadius(radius, dist_center);
gluLookAt(FakeEyePos[SG_X], FakeEyePos[SG_Z], FakeEyePos[SG_Y], center[SG_X], center[SG_Z], center[SG_Y], 0.0, 0.0, 1.0);
// draw into texture
Render3Dcloud(true, FakeEyePos, deltaPos, dist_center);
// save rotation angles for later use
// TODO:this is not ok
cldCache->setReference(cloudId, bbId, angleY, angleX);
// save the rendered cloud into the cache
cldCache->setTextureData( bbId );
// finish render to texture and go back into standard context
cldCache->endCapture();
}
// draw the newly built BB or an old one
glBindTexture(GL_TEXTURE_2D, texID);
RenderBB(deltaPos, angleY, angleX, dist_center);
}
}
}

View File

@@ -0,0 +1,147 @@
// 3D cloud class
//
// Written by Harald JOHNSEN, started April 2005.
//
// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
//
//
#ifndef _NEWCLOUD_HXX
#define _NEWCLOUD_HXX
#include <plib/sg.h>
#include <simgear/compiler.h>
#include STL_STRING
#include <vector>
#include "bbcache.hxx"
SG_USING_STD(string);
SG_USING_STD(vector);
class SGNewCloud {
public:
SGNewCloud();
~SGNewCloud();
enum CLbox_type {
CLbox_standard = 0,
CLbox_sc = 1,
CLbox_cumulus = 2,
CLbox_stratus = 3
};
enum CLTexture_type {
CLTexture_cumulus = 1,
CLTexture_stratus = 2,
CLTexture_max
};
private:
class spriteDef {
public:
sgVec3 pos;
float r;
CLbox_type sprite_type;
sgVec4 l0, l1, l2, l3;
int rank;
int box;
float dist; // distance used during sort
bool operator<(const spriteDef &b) const {
return this->dist < b.dist;
}
};
typedef struct {
sgVec3 pos;
float r;
// the type defines how the sprites can be positioned inside the box, their size, etc
CLbox_type cont_type;
sgVec3 center;
} spriteContainer;
typedef vector<spriteDef> list_of_spriteDef;
typedef vector<spriteContainer> list_of_spriteContainer;
void computeSimpleLight(sgVec3 eyePos);
void addSprite(float x, float y, float z, float r, CLbox_type type, int box);
// sort on distance to eye because of transparency
void sortSprite( sgVec3 eyePos );
// render the cloud on screen or on the RTT texture to build the impostor
void Render3Dcloud( bool drawBB, sgVec3 eyePos, sgVec3 deltaPos, float dist_center );
// compute rotations so that a quad is facing the camera
void CalcAngles(sgVec3 refpos, sgVec3 eyePos, float *angleY, float *angleX);
// draw a cloud but this time we use the impostor texture
void RenderBB(sgVec3 deltaPos, float angleY, float angleX, float dist_center);
// determine if it is a good idea to use an impostor to render the cloud
bool isBillboardable(float dist);
int cloudId, bbId;
// int rank;
sgVec3 cloudpos, center;
list_of_spriteDef list_spriteDef;
list_of_spriteContainer list_spriteContainer;
float minx, maxx, miny, maxy, minz, maxz;
float radius;
// fading data
bool direction, fadeActive;
float duration, pauseLength;
// need timer here
public:
// add a new box to the cloud
void addContainer(float x, float y, float z, float r, CLbox_type type);
// generate all sprite with defined boxes
void genSprites(void);
// debug only, define a cumulus
void new_cu(void);
// debug only
void drawContainers(void);
// define the new position of the cloud (inside the cloud field, not on sphere)
void SetPos(sgVec3 newPos);
// render the cloud, fakepos is a relative position inside the cloud field
void Render(sgVec3 fakepos);
//
void startFade(bool direction, float duration, float pauseLength);
void setFade(float howMuch);
inline float getRadius() { return radius; }
// load all textures used to draw cloud sprites
static void loadTextures( const string &tex_path );
static sgVec3 modelSunDir;
static sgVec3 sunlight, ambLight;
static bool useAnisotropic;
static float nearRadius;
static bool lowQuality;
static SGBbCache *cldCache;
};
#endif // _NEWCLOUD_HXX