From ad6129816d1c1d064748382547a1bc9f3d9a67c3 Mon Sep 17 00:00:00 2001 From: fredb Date: Sun, 26 Oct 2008 09:37:13 +0000 Subject: [PATCH] 3D clouds from Stuart Buchanan. Need a recent driver update, --enable-clouds3d option and a Weather Scenario to show up --- projects/VC7.1/SimGear.vcproj | 6 + simgear/environment/visual_enviro.cxx | 39 +- simgear/environment/visual_enviro.hxx | 3 +- simgear/scene/sky/CloudShaderGeometry.cxx | 139 +++ simgear/scene/sky/CloudShaderGeometry.hxx | 134 +++ simgear/scene/sky/Makefile.am | 6 +- simgear/scene/sky/cloud.cxx | 285 +----- simgear/scene/sky/cloud.hxx | 11 +- simgear/scene/sky/cloudfield.cxx | 531 +++-------- simgear/scene/sky/cloudfield.hxx | 88 +- simgear/scene/sky/newcloud.cxx | 1057 ++++++--------------- simgear/scene/sky/newcloud.hxx | 157 +-- simgear/scene/sky/sky.cxx | 42 +- simgear/scene/sky/sky.hxx | 28 +- 14 files changed, 866 insertions(+), 1660 deletions(-) create mode 100755 simgear/scene/sky/CloudShaderGeometry.cxx create mode 100755 simgear/scene/sky/CloudShaderGeometry.hxx diff --git a/projects/VC7.1/SimGear.vcproj b/projects/VC7.1/SimGear.vcproj index 957e2da6..c0efc9db 100755 --- a/projects/VC7.1/SimGear.vcproj +++ b/projects/VC7.1/SimGear.vcproj @@ -508,6 +508,12 @@ + + + + diff --git a/simgear/environment/visual_enviro.cxx b/simgear/environment/visual_enviro.cxx index dbc251e8..fb2cfa6d 100644 --- a/simgear/environment/visual_enviro.cxx +++ b/simgear/environment/visual_enviro.cxx @@ -203,8 +203,6 @@ void SGEnviro::startOfFrame( sgVec3 p, sgVec3 up, double lon, double lat, double return; view_in_cloud = false; // ask the impostor cache to do some cleanup - if(SGNewCloud::cldCache) - SGNewCloud::cldCache->startNewFrame(); last_cloud_turbulence = cloud_turbulence; cloud_turbulence = 0.0; elapsed_time += delta_time; @@ -255,43 +253,10 @@ void SGEnviro::set_view_in_cloud(bool incloud) { view_in_cloud = incloud; } -int SGEnviro::get_CacheResolution(void) const { - return SGCloudField::get_CacheResolution(); -} - -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(); -} - bool SGEnviro::get_turbulence_enable_state(void) const { return turbulence_enable_state; } -void SGEnviro::set_CacheResolution(int resolutionPixels) { - SGCloudField::set_CacheResolution(resolutionPixels); -} - -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); -} void SGEnviro::set_turbulence_enable_state(bool enable) { turbulence_enable_state = enable; } @@ -330,7 +295,7 @@ void SGEnviro::setLight(sgVec4 adj_fog_color) { // ssgGetLight( 0 ) -> setColour( GL_DIFFUSE, l->scene_diffuse() ); } } - +#if 0 void SGEnviro::callback_cloud(float heading, float alt, float radius, int family, float dist, int cloudId) { // send data to wx radar // compute turbulence @@ -441,6 +406,8 @@ void SGEnviro::callback_cloud(float heading, float alt, float radius, int family } } +#endif + list_of_SGWxRadarEcho *SGEnviro::get_radar_echo(void) { return &radarEcho; } diff --git a/simgear/environment/visual_enviro.hxx b/simgear/environment/visual_enviro.hxx index 42d2e3a5..51a76a06 100644 --- a/simgear/environment/visual_enviro.hxx +++ b/simgear/environment/visual_enviro.hxx @@ -125,6 +125,7 @@ public: void endOfFrame(void); +#if 0 /** * Whenever a cloud is drawn we check his 'impact' on the environment. * @param heading direction of cloud in radians @@ -134,7 +135,7 @@ public: * @param dist squared dist to cloud in meters */ void callback_cloud(float heading, float alt, float radius, int family, float dist, int cloudId); - +#endif void drawRain(double pitch, double roll, double heading, double hspeed, double rain_norm); /** * Draw rain or snow precipitation around the viewer. diff --git a/simgear/scene/sky/CloudShaderGeometry.cxx b/simgear/scene/sky/CloudShaderGeometry.cxx new file mode 100755 index 00000000..6fe9f8cf --- /dev/null +++ b/simgear/scene/sky/CloudShaderGeometry.cxx @@ -0,0 +1,139 @@ +/* -*-c++-*- + * + * Copyright (C) 2008 Stuart Buchanan + * + * 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, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#include +#include +#include + +#include "CloudShaderGeometry.hxx" + +#include + +using namespace osg; +using namespace osgDB; + +namespace simgear +{ +void CloudShaderGeometry::drawImplementation(RenderInfo& renderInfo) const +{ + osg::State& state = *renderInfo.getState(); + const Extensions* extensions = getExtensions(state.getContextID(),true); + + for(CloudSpriteList::const_iterator t = _cloudsprites.begin(); t != _cloudsprites.end(); ++t) + { + extensions->glVertexAttrib1f(TEXTURE_INDEX_X, (GLfloat) t->texture_index_x/varieties_x); + extensions->glVertexAttrib1f(TEXTURE_INDEX_Y, (GLfloat) t->texture_index_y/varieties_y); + extensions->glVertexAttrib1f(WIDTH, (GLfloat) t->width); + extensions->glVertexAttrib1f(HEIGHT, (GLfloat) t->height); + extensions->glVertexAttrib1f(SHADE, (GLfloat) t->shade); + glColor4f(t->position.x(), t->position.y(), t->position.z(), 1.0); + _geometry->draw(renderInfo); + } +} + +BoundingBox CloudShaderGeometry::computeBound() const +{ + BoundingBox geom_box = _geometry->getBound(); + BoundingBox bb; + for(CloudSpriteList::const_iterator itr = _cloudsprites.begin(); + itr != _cloudsprites.end(); + ++itr) { + bb.expandBy(geom_box.corner(0)*itr->width + + osg::Vec3( itr->position.x(), itr->position.y(), itr->position.z() )); + bb.expandBy(geom_box.corner(7)*itr->height + + osg::Vec3( itr->position.x(), itr->position.y(), itr->position.z() )); + } + return bb; +} + +bool CloudShaderGeometry_readLocalData(Object& obj, Input& fr) +{ + bool iteratorAdvanced = false; + + CloudShaderGeometry& geom = static_cast(obj); + + if ((fr[0].matchWord("geometry"))) { + ++fr; + iteratorAdvanced = true; + osg::Drawable* drawable = fr.readDrawable(); + if (drawable) { + geom._geometry = drawable; + } + } + if ((fr.matchSequence("instances %i"))) { + int entry = fr[0].getNoNestedBrackets(); + int capacity; + fr[1].getInt(capacity); + geom._cloudsprites.reserve(capacity); + fr += 3; + iteratorAdvanced = true; + // skip { + while (!fr.eof() && fr[0].getNoNestedBrackets() > entry) { + SGVec3f v; + int tx, ty; + float w, h, s; + if (fr[0].getFloat(v.x()) && fr[1].getFloat(v.y()) + && fr[2].getFloat(v.z()) && fr[3].getInt(tx) && fr[3].getInt(ty) && + fr[4].getFloat(w) && fr[4].getFloat(h)&& fr[4].getFloat(s)) { + fr += 5; + //SGVec3f* v = new SGVec3f(v.x(), v.y(), v.z()); + geom._cloudsprites.push_back(CloudShaderGeometry::CloudSprite(v, tx, ty, w, h,s)); + } else { + ++fr; + } + } + } + return iteratorAdvanced; +} + +bool CloudShaderGeometry_writeLocalData(const Object& obj, Output& fw) +{ + const CloudShaderGeometry& geom = static_cast(obj); + + fw.indent() << "geometry" << std::endl; + fw.writeObject(*geom._geometry); + fw.indent() << "instances " << geom._cloudsprites.size() << std::endl; + fw.indent() << "{" << std::endl; + fw.moveIn(); + for (CloudShaderGeometry::CloudSpriteList::const_iterator iter + = geom._cloudsprites.begin(); + iter != geom._cloudsprites.end(); + ++iter) { + fw.indent() << iter->position.x() << " " << iter->position.y() << " " + << iter->position.z() << " " << iter->texture_index_x << " " + << iter->texture_index_y << " " + << iter->width << " " << iter->height << " " << iter->shade << std::endl; + } + fw.moveOut(); + fw.indent() << "}" << std::endl; + return true; +} + + +osgDB::RegisterDotOsgWrapperProxy cloudShaderGeometryProxy +( + new CloudShaderGeometry, + "CloudShaderGeometry", + "Object Drawable CloudShaderGeometry", + &CloudShaderGeometry_readLocalData, + &CloudShaderGeometry_writeLocalData + ); +} diff --git a/simgear/scene/sky/CloudShaderGeometry.hxx b/simgear/scene/sky/CloudShaderGeometry.hxx new file mode 100755 index 00000000..e28928ed --- /dev/null +++ b/simgear/scene/sky/CloudShaderGeometry.hxx @@ -0,0 +1,134 @@ +/* -*-c++-*- + * + * Copyright (C) 2008 Stuart Buchanan + * + * 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, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + * + */ + +#ifndef CLOUD_SHADER_GEOMETRY_HXX +#define CLOUD_SHADER_GEOMETRY_HXX 1 + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + + +namespace simgear +{ + +class CloudShaderGeometry : public osg::Drawable +{ + public: + + const static unsigned int TEXTURE_INDEX_X = 11; + const static unsigned int TEXTURE_INDEX_Y = 12; + const static unsigned int WIDTH = 13; + const static unsigned int HEIGHT = 14; + const static unsigned int SHADE = 15; + + CloudShaderGeometry() + { + setUseDisplayList(false); + } + + CloudShaderGeometry(int vx, int vy) : + varieties_x(vx), varieties_y(vy) + { + setUseDisplayList(false); + } + + /** Copy constructor using CopyOp to manage deep vs shallow copy.*/ + CloudShaderGeometry(const CloudShaderGeometry& CloudShaderGeometry,const osg::CopyOp& copyop=osg::CopyOp::SHALLOW_COPY): + osg::Drawable(CloudShaderGeometry,copyop) {} + + META_Object(flightgear, CloudShaderGeometry); + + struct CloudSprite { + CloudSprite(const SGVec3f& p, int tx, int ty, float w, float h, float s) : + position(p), texture_index_x(tx), texture_index_y(ty), width(w), height(h), shade(s) + { } + + SGVec3f position; + int texture_index_x; + int texture_index_y; + float width; + float height; + float shade; + }; + + typedef std::vector CloudSpriteList; + + void insert(const CloudSprite& t) + { _cloudsprites.push_back(t); } + void insert(const SGVec3f& p, int tx, int ty, float w, float h, float s) + { insert(CloudSprite(p, tx, ty, w, h, s)); } + + unsigned getNumCloudSprite() const + { return _cloudsprites.size(); } + const CloudSprite& getCloudSprite(unsigned i) const + { return _cloudsprites[i]; } + CloudSpriteList _cloudsprites; + + typedef std::vector PositionSizeList; + + virtual void drawImplementation(osg::RenderInfo& renderInfo) const; + virtual osg::BoundingBox computeBound() const; + + + void setGeometry(osg::Drawable* geometry) + { + _geometry = geometry; + } + + void addSprite(const SGVec3f& p, int tx, int ty, float w, float h, float s, float cull) + { + // Only add the sprite if it is further than the cull distance to all other sprites + for (CloudShaderGeometry::CloudSpriteList::const_iterator iter = _cloudsprites.begin(); + iter != _cloudsprites.end(); + ++iter) + { + if (distSqr(iter->position, p) < cull) + { + // Too close - cull it + return; + } + } + + _cloudsprites.push_back(CloudSprite(p, tx, ty, w, h, s)); + } + + osg::ref_ptr _geometry; + + int varieties_x; + int varieties_y; + + protected: + + virtual ~CloudShaderGeometry() {} + +}; + +} +#endif diff --git a/simgear/scene/sky/Makefile.am b/simgear/scene/sky/Makefile.am index 542ce3f4..104ca4e1 100644 --- a/simgear/scene/sky/Makefile.am +++ b/simgear/scene/sky/Makefile.am @@ -12,7 +12,8 @@ include_HEADERS = \ stars.hxx \ bbcache.hxx \ cloudfield.hxx \ - newcloud.hxx + newcloud.hxx \ + CloudShaderGeometry.hxx libsgsky_a_SOURCES = \ cloud.cxx \ @@ -24,6 +25,7 @@ libsgsky_a_SOURCES = \ stars.cxx \ bbcache.cxx \ cloudfield.cxx \ - newcloud.cxx + newcloud.cxx \ + CloudShaderGeometry.cxx INCLUDES = -I$(top_srcdir) diff --git a/simgear/scene/sky/cloud.cxx b/simgear/scene/sky/cloud.cxx index 4a8c1aea..0ec7139b 100644 --- a/simgear/scene/sky/cloud.cxx +++ b/simgear/scene/sky/cloud.cxx @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -127,6 +128,7 @@ SGMakeState(const SGPath &path, const char* colorTexture, // Constructor SGCloudLayer::SGCloudLayer( const string &tex_path ) : + cloud_root(new osg::Switch), layer_root(new osg::Switch), group_top(new osg::Group), group_bottom(new osg::Group), @@ -149,6 +151,7 @@ SGCloudLayer::SGCloudLayer( const string &tex_path ) : // in bin 10), tops after. The negative numbers on the bottoms // RenderBins and the positive numbers on the tops enforce this // order. + cloud_root->addChild(layer_root.get(), true); layer_root->addChild(group_bottom.get()); layer_root->addChild(group_top.get()); osg::StateSet *rootSet = layer_root->getOrCreateStateSet(); @@ -192,7 +195,9 @@ SGCloudLayer::SGCloudLayer( const string &tex_path ) : group_top->addChild(layer_transform.get()); group_bottom->addChild(layer_transform.get()); - layer3D = new SGCloudField; + layer3D = new SGCloudField(); + cloud_root->addChild(layer3D->getNode(), false); + rebuild(); } @@ -452,10 +457,6 @@ SGCloudLayer::rebuild() layer_states[SG_CLOUD_CLEAR] = 0; layer_states2[SG_CLOUD_CLEAR] = 0; - - // OSGFIXME -// SGNewCloud::loadTextures(texture_path.str()); -// layer3D->buildTestLayer(); } scale = 4000.0; @@ -560,259 +561,6 @@ SGCloudLayer::rebuild() } } -#if 0 - sgMat4 modelview, - tmp, - transform; - ssgGetModelviewMatrix( modelview ); - layer_transform->getTransform( transform ); - - sgTransposeNegateMat4( tmp, transform ); - - sgPostMultMat4( transform, modelview ); - ssgLoadModelviewMatrix( transform ); - - sgVec3 lightVec; - ssgGetLight( 0 )->getPosition( lightVec ); - sgNegateVec3( lightVec ); - sgXformVec3( lightVec, tmp ); - - for ( int i = 0; i < 25; i++ ) { - CloudVertex &v = vertices[ i ]; - sgSetVec3( v.tangentSpLight, - sgScalarProductVec3( v.sTangent, lightVec ), - sgScalarProductVec3( v.tTangent, lightVec ), - sgScalarProductVec3( v.normal, lightVec ) ); - } - - ssgTexture *decal = color_map[ layer_coverage ][ top ? 1 : 0 ]; - if ( top && decal == 0 ) { - decal = color_map[ layer_coverage ][ 0 ]; - } - ssgTexture *normal = normal_map[ layer_coverage ][ top ? 1 : 0 ]; - if ( top && normal == 0 ) { - normal = normal_map[ layer_coverage ][ 0 ]; - } - - glDisable( GL_LIGHTING ); - glDisable( GL_CULL_FACE ); -// glDisable( GL_ALPHA_TEST ); - if ( layer_coverage == SG_CLOUD_FEW ) { - glEnable( GL_ALPHA_TEST ); - glAlphaFunc ( GL_GREATER, 0.01 ); - } - glEnable( GL_BLEND ); - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - - glShadeModel( GL_SMOOTH ); - glEnable( GL_COLOR_MATERIAL ); - sgVec4 color; - float emis = 0.05; - if ( 1 ) { - ssgGetLight( 0 )->getColour( GL_DIFFUSE, color ); - emis = ( color[0]+color[1]+color[2] ) / 3.0; - if ( emis < 0.05 ) - emis = 0.05; - } - sgSetVec4( color, emis, emis, emis, 0.0 ); - glMaterialfv( GL_FRONT_AND_BACK, GL_EMISSION, color ); - sgSetVec4( color, 1.0f, 1.0f, 1.0f, 0.0 ); - glMaterialfv( GL_FRONT_AND_BACK, GL_AMBIENT, color ); - sgSetVec4( color, 1.0, 1.0, 1.0, 0.0 ); - glMaterialfv( GL_FRONT_AND_BACK, GL_DIFFUSE, color ); - sgSetVec4( color, 0.0, 0.0, 0.0, 0.0 ); - glMaterialfv( GL_FRONT_AND_BACK, GL_SPECULAR, color ); - - glColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); - - glActiveTexturePtr( GL_TEXTURE0_ARB ); - glBindTexture( GL_TEXTURE_2D, normal->getHandle() ); - glEnable( GL_TEXTURE_2D ); - - //Bind normalisation cube map to texture unit 1 - glActiveTexturePtr( GL_TEXTURE1_ARB ); - glBindTexture( GL_TEXTURE_CUBE_MAP_ARB, normalization_cube_map ); - glEnable( GL_TEXTURE_CUBE_MAP_ARB ); - glActiveTexturePtr( GL_TEXTURE0_ARB ); - - //Set vertex arrays for cloud - glVertexPointer( 3, GL_FLOAT, sizeof(CloudVertex), &vertices[0].position ); - glEnableClientState( GL_VERTEX_ARRAY ); -/* - if ( nb_texture_unit >= 3 ) { - glColorPointer( 4, GL_FLOAT, sizeof(CloudVertex), &vertices[0].color ); - glEnableClientState( GL_COLOR_ARRAY ); - } -*/ - //Send texture coords for normal map to unit 0 - glTexCoordPointer( 2, GL_FLOAT, sizeof(CloudVertex), &vertices[0].texCoord ); - glEnableClientState( GL_TEXTURE_COORD_ARRAY ); - - //Send tangent space light vectors for normalisation to unit 1 - glClientActiveTexturePtr( GL_TEXTURE1_ARB ); - glTexCoordPointer( 3, GL_FLOAT, sizeof(CloudVertex), &vertices[0].tangentSpLight ); - glEnableClientState( GL_TEXTURE_COORD_ARRAY ); - - //Set up texture environment to do (tex0 dot tex1)*color - glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB ); - glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE ); - glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE ); - glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_TEXTURE ); - glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE ); - -// use TexEnvCombine to add the highlights to the original lighting -osg::TexEnvCombine *te = new osg::TexEnvCombine; -te->setSource0_RGB(osg::TexEnvCombine::TEXTURE); -te->setCombine_RGB(osg::TexEnvCombine::REPLACE); -te->setSource0_Alpha(osg::TexEnvCombine::TEXTURE); -te->setCombine_Alpha(osg::TexEnvCombine::REPLACE); -ss->setTextureAttributeAndModes(0, te, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); - - - glActiveTexturePtr( GL_TEXTURE1_ARB ); - - glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB ); - glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE ); - glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB ); - glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB ); - glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_ALPHA_ARB, GL_PREVIOUS_ARB ); - glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_ALPHA_ARB, GL_REPLACE ); - -osg::TexEnvCombine *te = new osg::TexEnvCombine; -te->setSource0_RGB(osg::TexEnvCombine::TEXTURE); -te->setCombine_RGB(osg::TexEnvCombine::DOT3_RGB); -te->setSource1_RGB(osg::TexEnvCombine::PREVIOUS); -te->setSource0_Alpha(osg::TexEnvCombine::PREVIOUS); -te->setCombine_Alpha(osg::TexEnvCombine::REPLACE); -ss->setTextureAttributeAndModes(0, te, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON); - - - if ( nb_texture_unit >= 3 ) { - glActiveTexturePtr( GL_TEXTURE2_ARB ); - glBindTexture( GL_TEXTURE_2D, decal->getHandle() ); - - glClientActiveTexturePtr( GL_TEXTURE2_ARB ); - glTexCoordPointer( 2, GL_FLOAT, sizeof(CloudVertex), &vertices[0].texCoord ); - glEnableClientState( GL_TEXTURE_COORD_ARRAY ); - - glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB ); - glTexEnvi( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_ADD ); - glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE ); - glTexEnvi( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB ); - - glClientActiveTexturePtr( GL_TEXTURE0_ARB ); - glActiveTexturePtr( GL_TEXTURE0_ARB ); - - //Draw cloud layer - glDrawElements( GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, &indices[0] ); - glDrawElements( GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, &indices[10] ); - glDrawElements( GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, &indices[20] ); - glDrawElements( GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, &indices[30] ); - - glDisable( GL_TEXTURE_2D ); - glActiveTexturePtr( GL_TEXTURE1_ARB ); - glDisable( GL_TEXTURE_CUBE_MAP_ARB ); - glActiveTexturePtr( GL_TEXTURE2_ARB ); - glDisable( GL_TEXTURE_2D ); - glActiveTexturePtr( GL_TEXTURE0_ARB ); - - glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - glClientActiveTexturePtr( GL_TEXTURE1_ARB ); - glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - glClientActiveTexturePtr( GL_TEXTURE2_ARB ); - glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - glClientActiveTexturePtr( GL_TEXTURE0_ARB ); - - glDisableClientState( GL_COLOR_ARRAY ); - glEnable( GL_LIGHTING ); - - glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - - } else { - glClientActiveTexturePtr( GL_TEXTURE0_ARB ); - glActiveTexturePtr( GL_TEXTURE0_ARB ); - - //Draw cloud layer - glDrawElements( GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, &indices[0] ); - glDrawElements( GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, &indices[10] ); - glDrawElements( GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, &indices[20] ); - glDrawElements( GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, &indices[30] ); - - //Disable textures - glDisable( GL_TEXTURE_2D ); - - glActiveTexturePtr( GL_TEXTURE1_ARB ); - glDisable( GL_TEXTURE_CUBE_MAP_ARB ); - glActiveTexturePtr( GL_TEXTURE0_ARB ); - - //disable vertex arrays - glDisableClientState( GL_VERTEX_ARRAY ); - - glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - glClientActiveTexturePtr( GL_TEXTURE1_ARB ); - glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - glClientActiveTexturePtr( GL_TEXTURE0_ARB ); - - //Return to standard modulate texenv - glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - - if ( layer_coverage == SG_CLOUD_OVERCAST ) { - glDepthFunc(GL_LEQUAL); - - glEnable( GL_LIGHTING ); - sgVec4 color; - ssgGetLight( 0 )->getColour( GL_DIFFUSE, color ); - float average = ( color[0] + color[1] + color[2] ) / 3.0f; - average = 0.15 + average/10; - sgVec4 averageColor; - sgSetVec4( averageColor, average, average, average, 1.0f ); - ssgGetLight( 0 )->setColour( GL_DIFFUSE, averageColor ); - - glBlendColorPtr( average, average, average, 1.0f ); - glBlendFunc( GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_COLOR ); - - //Perform a second pass to color the torus - //Bind decal texture - glBindTexture( GL_TEXTURE_2D, decal->getHandle() ); - glEnable(GL_TEXTURE_2D); - - //Set vertex arrays for torus - glVertexPointer( 3, GL_FLOAT, sizeof(CloudVertex), &vertices[0].position ); - glEnableClientState( GL_VERTEX_ARRAY ); - - //glColorPointer( 4, GL_FLOAT, sizeof(CloudVertex), &vertices[0].color ); - //glEnableClientState( GL_COLOR_ARRAY ); - - glNormalPointer( GL_FLOAT, sizeof(CloudVertex), &vertices[0].normal ); - glEnableClientState( GL_NORMAL_ARRAY ); - - glTexCoordPointer( 2, GL_FLOAT, sizeof(CloudVertex), &vertices[0].texCoord ); - glEnableClientState( GL_TEXTURE_COORD_ARRAY ); - - //Draw cloud layer - glDrawElements( GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, &indices[0] ); - glDrawElements( GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, &indices[10] ); - glDrawElements( GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, &indices[20] ); - glDrawElements( GL_TRIANGLE_STRIP, 10, GL_UNSIGNED_INT, &indices[30] ); - - ssgGetLight( 0 )->setColour( GL_DIFFUSE, color ); - - glDisableClientState( GL_TEXTURE_COORD_ARRAY ); - } - } - //Disable texture - glDisable( GL_TEXTURE_2D ); - - glDisableClientState( GL_VERTEX_ARRAY ); - glDisableClientState( GL_NORMAL_ARRAY ); - - glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - glEnable( GL_CULL_FACE ); - glDepthFunc(GL_LESS); - - ssgLoadModelviewMatrix( modelview ); -#endif - // repaint the cloud layer colors bool SGCloudLayer::repaint( const SGVec3f& fog_color ) { osg::Vec4f combineColor(fog_color.osg(), cloud_alpha); @@ -820,6 +568,9 @@ bool SGCloudLayer::repaint( const SGVec3f& fog_color ) { = dynamic_cast(layer_root->getStateSet() ->getTextureAttribute(1, osg::StateAttribute::TEXENV)); combiner->setConstantColor(combineColor); + + // Set the fog color for the 3D clouds too. + //cloud3dfog->setColor(combineColor); return true; } @@ -865,6 +616,7 @@ bool SGCloudLayer::reposition( const SGVec3f& p, const SGVec3f& up, double lon, LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0)); layer_transform->setMatrix( LAT*LON*T ); + // The layers need to be drawn in order because they are // translucent, but OSG transparency sorting doesn't work because // the cloud polys are huge. However, the ordering is simple: the @@ -967,6 +719,21 @@ bool SGCloudLayer::reposition( const SGVec3f& p, const SGVec3f& up, double lon, last_lat = lat; } -// layer3D->reposition( p, up, lon, lat, alt, dt, direction, speed); + layer3D->reposition( p, up, lon, lat, dt); return true; } + +void SGCloudLayer::set_enable3dClouds(bool enable) { + + if (layer3D->defined3D && enable) { + cloud_root->setChildValue(layer3D->getNode(), true); + cloud_root->setChildValue(layer_root.get(), false); + } else { + cloud_root->setChildValue(layer3D->getNode(), false); + cloud_root->setChildValue(layer_root.get(), true); + } +} + +void SGCloudLayer::applyDensity() { + layer3D->applyDensity(); +} diff --git a/simgear/scene/sky/cloud.hxx b/simgear/scene/sky/cloud.hxx index fd0722c6..4a12cfd5 100644 --- a/simgear/scene/sky/cloud.hxx +++ b/simgear/scene/sky/cloud.hxx @@ -162,6 +162,12 @@ public: /** build the cloud object */ void rebuild(); + /** Enable/disable 3D clouds in this layer */ + void set_enable3dClouds(bool enable); + + /** Set 3D cloud density in this layer */ + void applyDensity(); + /** * repaint the cloud colors based on the specified fog_color * @param fog_color the fog color @@ -183,16 +189,18 @@ public: double lon, double lat, double alt, double dt = 0.0 ); - osg::Switch* getNode() { return layer_root.get(); } + osg::Switch* getNode() { return cloud_root.get(); } static bool enable_bump_mapping; /** return the 3D layer cloud associated with this 2D layer */ SGCloudField *get_layer3D(void) { return layer3D; } + protected: void setTextureOffset(const osg::Vec2& offset); private: + osg::ref_ptr cloud_root; osg::ref_ptr layer_root; osg::ref_ptr group_top, group_bottom; osg::ref_ptr layer_transform; @@ -225,6 +233,7 @@ private: osg::Vec2 base; SGCloudField *layer3D; + }; #endif // _SG_CLOUD_HXX_ diff --git a/simgear/scene/sky/cloudfield.cxx b/simgear/scene/sky/cloudfield.cxx index d0097dcb..f23b0f5c 100644 --- a/simgear/scene/sky/cloudfield.cxx +++ b/simgear/scene/sky/cloudfield.cxx @@ -24,6 +24,9 @@ # include #endif +#include +#include + #include #include @@ -58,216 +61,118 @@ using std::vector; #include #endif -static list_of_culledCloud inViewClouds; - -// visibility distance for clouds in meters -float SGCloudField::CloudVis = 25000.0f; -bool SGCloudField::enable3D = false; -// 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 = 50000.0; float SGCloudField::density = 100.0; double SGCloudField::timer_dt = 0.0; sgVec3 SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y; -static int last_cache_size = 1*1024; -static int cacheResolution = 64; -static sgVec3 last_sunlight={0.0f, 0.0f, 0.0f}; - -int SGCloudField::get_CacheResolution(void) { -#if 0 - return cacheResolution; -#endif - return 0; -} - -void SGCloudField::set_CacheResolution(int resolutionPixels) { -#if 0 - if(cacheResolution == resolutionPixels) - return; - cacheResolution = resolutionPixels; - if(enable3D) { - int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4); - if(count == 0) - count = 1; - SGNewCloud::cldCache->setCacheSize(count, cacheResolution); - } -#endif -} - -int SGCloudField::get_CacheSize(void) { -#if 0 - return SGNewCloud::cldCache->queryCacheSize(); -#endif - return 0; -} - -void SGCloudField::set_CacheSize(int sizeKb) { -#if 0 - // apply in rendering option dialog - if(last_cache_size == sizeKb) - return; - if(sizeKb == 0) - return; - if(sizeKb) - last_cache_size = sizeKb; - if(enable3D) { - int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4); - if(count == 0) - count = 1; - SGNewCloud::cldCache->setCacheSize(count, cacheResolution); - } -#endif -} -void SGCloudField::set_CloudVis(float distance) { -#if 0 - if( distance <= fieldSize ) - SGCloudField::CloudVis = distance; -#endif -} void SGCloudField::set_density(float density) { -#if 0 SGCloudField::density = density; -#endif -} -void SGCloudField::set_enable3dClouds(bool enable) { -#if 0 - if(enable3D == enable) - return; - enable3D = enable; - if(enable) { - int count = last_cache_size * 1024 / (cacheResolution * cacheResolution * 4); - if(count == 0) - count = 1; - SGNewCloud::cldCache->setCacheSize(count, cacheResolution); - } else { - SGNewCloud::cldCache->setCacheSize(0); - } -#endif } // 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, float direction, float speed) { -#if 0 - 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 ); - - - this->alt = alt; - - // simulate clouds movement from wind - 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; - } - - if ( lon != last_lon || lat != last_lat || sp_dist != 0 ) { - Point3D start( last_lon, last_lat, 0.0 ); - Point3D dest( lon, lat, 0.0 ); - double course = 0.0, dist = 0.0; - - calc_gc_course_dist( dest, start, &course, &dist ); - // if start and dest are too close together, - // calc_gc_course_dist() can return a course of "nan". If - // this happens, lets just use the last known good course. - // This is a hack, and it would probably be better to make - // calc_gc_course_dist() more robust. - if ( isnan(course) ) { - course = last_course; - } else { - last_course = course; +bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, double lat, + double dt ) +{ + osg::Matrix T, LON, LAT; + + LON.makeRotate(lon, osg::Vec3(0, 0, 1)); + LAT.makeRotate(90.0 * SGD_DEGREES_TO_RADIANS - lat, osg::Vec3(0, 1, 0)); + + osg::Vec3 u = up.osg(); + u.normalize(); + + if ((last_lon == 0.0f) || (fabs(last_lon - lon) > 1.0) || (fabs(last_lat - lat) > 1.0)) + { + // First time, or large delta requires repositioning from scratch. + // TODO: Make this calculation better - a 0.5 degree shift will take a number + // of reposition calls to correct at the moment. + + // combine p and asl (meters) to get translation offset. + osg::Vec3 pos = p.osg(); + pos += u; + + T.makeTranslate(pos); + + field_transform->setMatrix( LAT*LON*T ); + last_lon = lon; + last_lat = lat; + last_pos = p.osg(); } - - // calculate cloud movement due to external forces - double ax = 0.0, ay = 0.0; - - if (dist > 0.0) { - ax = cos(course) * dist; - ay = sin(course) * dist; + else + { + // Rotation positon back to simple X-Z space. + osg::Vec3 pos = last_pos; + pos += u; + + T.makeTranslate(pos); + + osg::Matrix U = LAT*LON; + osg::Vec3 x = osg::Vec3f(fieldSize, 0.0, 0.0)*U; + osg::Vec3 y = osg::Vec3f(0.0, fieldSize, 0.0)*U; + + osg::Matrix V; + V.makeIdentity(); + V.invert(U*T); + + osg::Vec3 q = pos*V; + + // Shift the field if we've moved away from the centre box. + if (q.x() > fieldSize) last_pos = last_pos + x; + if (q.x() < -fieldSize) last_pos = last_pos - x; + if (q.y() > fieldSize) last_pos = last_pos + y; + if (q.y() < -fieldSize) last_pos = last_pos - y; + + pos = last_pos; + pos += u; + + T.makeTranslate(pos); + field_transform->setMatrix( LAT*LON*T ); } - - deltax += ax; - deltay += ay; - - last_lon = lon; - last_lat = lat; - } - - - // correct the frustum with the right far plane - ssgContext *context = ssgGetCurrentContext(); - frustum = *context->getFrustum(); - - float w, h; - sgEnviro.getFOV( w, h ); - frustum.setFOV( w, h ); - frustum.setNearFar(1.0, CloudVis); - timer_dt = dt; -#endif + return true; } SGCloudField::SGCloudField() : + field_root(new osg::Group), + field_transform(new osg::MatrixTransform), + field_group(new osg::Switch), deltax(0.0), deltay(0.0), + last_lon(0.0), + last_lat(0.0), last_course(0.0), last_density(0.0), - draw_in_3d(true) + defined3D(false) { -#if 0 - sgSetVec3( relative_position, 0,0,0); - theField.reserve(200); - inViewClouds.reserve(200); - sg_srandom_time_10(); -#else - draw_in_3d = false; -#endif + field_root->addChild(field_transform.get()); + field_group->setName("3Dcloud"); + + // We duplicate the defined field group in a 3x3 array. This way, + // we can simply shift entire groups around. + for(int x = -1 ; x <= 1 ; x++) { + for(int y = -1 ; y <= 1 ; y++ ) { + osg::ref_ptr transform = + new osg::PositionAttitudeTransform; + transform->addChild(field_group.get()); + transform->setPosition(osg::Vec3(x*fieldSize, y * fieldSize, 0.0)); + + field_transform->addChild(transform.get()); + } + } + } SGCloudField::~SGCloudField() { -#if 0 - list_of_Cloud::iterator iCloud; - for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) { - delete iCloud->aCloud; - } - theField.clear(); -#endif } void SGCloudField::clear(void) { -#if 0 - list_of_Cloud::iterator iCloud; - for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) { - delete iCloud->aCloud; - } - theField.clear(); - // force a recompute of density on first redraw - last_density = 0.0; - // true to come back in set density after layer is built - draw_in_3d = true; -#endif + int num_children = field_group->getNumChildren(); + + for (int i = 0; i < num_children; i++) { + field_group->removeChild(i); + } + SGCloudField::defined3D = false; } // use a table or else we see poping when moving the slider... @@ -285,248 +190,34 @@ static int densTable[][10] = { {1,1,1,1,1,1,1,1,1,1} }; -// set the visible flag depending on density void SGCloudField::applyDensity(void) { -#if 0 - int row = (int) (density / 10.0); - int col = 0; - sgBox fieldBox; - - list_of_Cloud::iterator iCloud; - for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) { - if(++col > 9) - col = 0; - if( densTable[row][col] ) { - iCloud->visible = true; - fieldBox.extend( *iCloud->aCloud->getCenter() ); - } else - iCloud->visible = false; - } - last_density = density; - draw_in_3d = ( theField.size() != 0); - sgVec3 center; - sgSubVec3( center, fieldBox.getMax(), fieldBox.getMin() ); - sgScaleVec3( center, 0.5f ); - center[1] = 0.0f; - field_sphere.setCenter( center ); - field_sphere.setRadius( fieldSize * 0.5f * 1.414f ); -#endif -} - -// add one cloud, data is not copied, ownership given -void SGCloudField::addCloud( sgVec3 pos, SGNewCloud *cloud) { -#if 0 - Cloud cl; - cl.aCloud = cloud; - cl.visible = true; - cloud->SetPos( pos ); - sgCopyVec3( cl.pos, *cloud->getCenter() ); - theField.push_back( cl ); -#endif -} + int row = (int) (density / 10.0); + int col = 0; + int num_children = field_group->getNumChildren(); + + if (density != last_density) { + // Switch on/off the children depending on the required density. + for (int i = 0; i < num_children; i++) { + if (++col > 9) col = 0; + if ( densTable[row][col] ) { + field_group->setValue(i, true); + } else { + field_group->setValue(i, false); + } + } + } -static float Rnd(float n) { - return n * (-0.5f + sg_random()); + last_density = density; } -// for debug only -// build a field of cloud of size 25x25 km, its a grid of 11x11 clouds -void SGCloudField::buildTestLayer(void) { -#if 0 - const float s = 2250.0f; - - for( int z = -5 ; z <= 5 ; z++) { - for( int x = -5 ; x <= 5 ; x++ ) { - SGNewCloud *cloud = new SGNewCloud(SGNewCloud::CLFamilly_cu); - cloud->new_cu(); - sgVec3 pos = {(x+Rnd(0.7)) * s, 750.0f, (z+Rnd(0.7)) * s}; - addCloud(pos, cloud); - } - } - applyDensity(); -#endif -} - -// cull all clouds of a tiled field -void SGCloudField::cullClouds(sgVec3 eyePos, sgMat4 mat) { -#if 0 - list_of_Cloud::iterator iCloud; - - sgSphere tile_sphere; - tile_sphere.setRadius( field_sphere.getRadius() ); - sgVec3 tile_center; - sgSubVec3( tile_center, field_sphere.getCenter(), eyePos ); - tile_sphere.setCenter( tile_center ); - tile_sphere.orthoXform(mat); - if( frustum.contains( & tile_sphere ) == SG_OUTSIDE ) - return; - - for( iCloud = theField.begin() ; iCloud != theField.end() ; iCloud++ ) { - sgVec3 dist; - sgSphere sphere; - if( ! iCloud->visible ) - continue; - sgSubVec3( dist, iCloud->pos, eyePos ); - sphere.setCenter(dist[0], dist[2], dist[1] + eyePos[1]); - float radius = iCloud->aCloud->getRadius(); - sphere.setRadius(radius); - 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; - tmp.heading = -SG_PI/2.0 - atan2( dist[0], dist[2] ); // + SG_PI; - tmp.alt = iCloud->pos[1]; - inViewClouds.push_back(tmp); - if( squareDist - radius*radius < 100*100 ) - sgEnviro.set_view_in_cloud(true); - } - } -#endif -} +void SGCloudField::addCloud( SGVec3f& pos, SGNewCloud *cloud) { + defined3D = true; + osg::ref_ptr lod = cloud->genCloud(); + osg::ref_ptr transform = new osg::PositionAttitudeTransform; + transform->setPosition(pos.osg()); + transform->addChild(lod.get()); -// 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(float *sun_color) { - // sun_color used to depend on an extern SGSky *thesky definition - // above. However, this is bad form for a library and it's much - // more clean to just pass in the needed value. For reference, here is - // the old way that sun_color was fetched ... - // float *sun_color = thesky->get_sun_color(); - -#if 0 - sgVec3 eyePos; - double relx, rely; - - if( ! enable3D ) - return; - - if( last_density != density ) { - last_density = density; - applyDensity(); - } - - if( ! draw_in_3d ) - return; - - if( ! SGNewCloud::cldCache->isRttAvailable() ) - return; - - inViewClouds.clear(); - - - glPushMatrix(); - - sgMat4 modelview, tmp, invtrans; - - // try to find the sun position - sgTransposeNegateMat4( invtrans, transform ); - sgVec3 lightVec; - ssgGetLight( 0 )->getPosition( lightVec ); - sgXformVec3( lightVec, invtrans ); - - sgSetVec3( SGNewCloud::modelSunDir, lightVec[0], lightVec[2], lightVec[1]); - // try to find the lighting data (not accurate) - sgVec4 diffuse, ambient; - ssgGetLight( 0 )->getColour( GL_DIFFUSE, diffuse ); - ssgGetLight( 0 )->getColour( GL_AMBIENT, ambient ); -// sgScaleVec3 ( SGNewCloud::sunlight, diffuse , 1.0f); - sgScaleVec3 ( SGNewCloud::ambLight, ambient , 1.1f); - // trying something else : clouds are more yellow/red at dawn/dusk - // and added a bit of blue ambient - sgScaleVec3 ( SGNewCloud::sunlight, sun_color , 0.4f); - SGNewCloud::ambLight[2] += 0.1f; - - sgVec3 delta_light; - sgSubVec3(delta_light, last_sunlight, SGNewCloud::sunlight); - if( (fabs(delta_light[0]) + fabs(delta_light[1]) + fabs(delta_light[2])) > 0.05f ) { - sgCopyVec3( last_sunlight, SGNewCloud::sunlight ); - // force the redraw of all the impostors - SGNewCloud::cldCache->invalidateCache(); - } - - // voodoo things on the matrix stack - ssgGetModelviewMatrix( modelview ); - sgCopyMat4( tmp, transform ); - sgPostMultMat4( tmp, modelview ); - - // 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 ); - - relx = fmod( relx + fieldSize, fieldSize ); - rely = fmod( rely + fieldSize, fieldSize ); - sgSetVec3( eyePos, relx, alt, rely); - - sgSetVec3( view_X, tmp[0][0], tmp[1][0], tmp[2][0] ); - sgSetVec3( view_Y, tmp[0][1], tmp[1][1], tmp[2][1] ); - sgSetVec3( view_vec, tmp[0][2], tmp[1][2], tmp[2][2] ); - - 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) - std::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); - glEnable(GL_DEPTH_TEST); - glDepthMask( GL_FALSE ); - glEnable(GL_SMOOTH); - glEnable(GL_BLEND); - glBlendFunc( GL_ONE, GL_ONE_MINUS_SRC_ALPHA ); - 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); - sgEnviro.callback_cloud(iCloud->heading, iCloud->alt, - iCloud->aCloud->getRadius(), iCloud->aCloud->getFamilly(), - iCloud->dist, iCloud->aCloud->getId()); - } - - glBindTexture(GL_TEXTURE_2D, 0); - glBlendFunc ( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ) ; - glEnable( GL_FOG ); - glEnable(GL_CULL_FACE); - glEnable(GL_DEPTH_TEST); - - ssgLoadModelviewMatrix( modelview ); - - glPopMatrix(); -#endif + field_group->addChild(transform.get()); } diff --git a/simgear/scene/sky/cloudfield.hxx b/simgear/scene/sky/cloudfield.hxx index fc358688..dd43669b 100644 --- a/simgear/scene/sky/cloudfield.hxx +++ b/simgear/scene/sky/cloudfield.hxx @@ -26,25 +26,22 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include + +#include using std::vector; class SGNewCloud; -class culledCloud { -public: - SGNewCloud *aCloud; - sgVec3 eyePos; - float dist; - float heading; - float alt; - bool operator<(const culledCloud &b) const { - return (this->dist < b.dist); - } -}; -typedef vector list_of_culledCloud; - /** * A layer of 3D clouds. */ @@ -58,27 +55,21 @@ private: bool visible; }; + float Rnd(float); - typedef vector list_of_Cloud; - - // cull all clouds of a tiled field - void cullClouds(sgVec3 eyePos, sgMat4 mat); - - void applyDensity(void); - - 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; + // double lon, lat; - sgFrustum frustum; + osg::ref_ptr field_root; + osg::ref_ptr field_transform; + osg::ref_ptr field_group; - sgMat4 transform; double deltax, deltay, alt; - double last_lon, last_lat, last_course; - sgSphere field_sphere; + double last_lon, last_lat, last_course; + sgSphere field_sphere; float last_density; - bool draw_in_3d; + osg::Vec3 last_pos; public: @@ -88,18 +79,23 @@ public: void clear(void); // 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( float *sun_color ); - - // reposition the cloud layer at the specified origin and orientation - void reposition( sgVec3 p, sgVec3 up, double lon, double lat, double alt, double dt, float direction, float speed); - - bool is3D(void) { return draw_in_3d; } + void addCloud( SGVec3f& pos, SGNewCloud *cloud); + + /** + * reposition the cloud layer at the specified origin and + * orientation. + * @param p position vector + * @param up the local up vector + * @param lon specifies a rotation about the Z axis + * @param lat specifies a rotation about the new Y axis + * @param spin specifies a rotation about the new Z axis + * (and orients the sunrise/set effects) + * @param dt the time elapsed since the last call + */ + bool reposition( const SGVec3f& p, const SGVec3f& up, + double lon, double lat, double dt = 0.0 ); + + osg::Group* getNode() { return field_root.get(); } // visibility distance for clouds in meters static float CloudVis; @@ -109,20 +105,14 @@ public: static float density; static double timer_dt; static double fieldSize; - static bool enable3D; + + bool defined3D; - // return the size of the memory pool used by texture impostors - static int get_CacheSize(void); - static int get_CacheResolution(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_CacheResolution(int resolutionPixels); - static void set_CloudVis(float distance); static void set_density(float density); - static void set_enable3dClouds(bool enable); + + void applyDensity(void); }; #endif // _CLOUDFIELD_HXX diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx index e18475c3..1ab787e6 100644 --- a/simgear/scene/sky/newcloud.cxx +++ b/simgear/scene/sky/newcloud.cxx @@ -24,793 +24,322 @@ # include #endif +#include +#include +#include #include #include +#include +#include +#include +#include +#include +#include + #include #include #include #include +#include +#include +#include +#include #include #include #include "cloudfield.hxx" #include "newcloud.hxx" - - -/* -*/ - -static osg::ref_ptr cloudTextures[SGNewCloud::CLTexture_max]; - - -bool SGNewCloud::useAnisotropic = true; -SGBbCache *SGNewCloud::cldCache = 0; -static bool texturesLoaded = false; -static float minx, maxx, miny, maxy, minz, maxz; -static int cloudIdCounter = 1; - -float SGNewCloud::nearRadius = 3500.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}; - - -void SGNewCloud::init(void) { - bbId = -1; - fadeActive = false; - duration = 100.0f; - fadetimer = 100.0f; - pauseLength = 0.0f; - last_step = -1.0f; - familly = CLFamilly_nn; - cloudId = ++cloudIdCounter; - sgSetVec3(center, 0.0f, 0.0f, 0.0f); - sgSetVec3(cloudpos, 0.0f, 0.0f, 0.0f); - radius = 0.0f; - delta_base = 0.0f; - list_spriteContainer.reserve(8); - list_spriteDef.reserve(40); - - if( cldCache == 0 ) { - cldCache = new SGBbCache; - cldCache->init( 64 ); - } -} - -// constructor -SGNewCloud::SGNewCloud(CLFamilly_type classification) +#include "CloudShaderGeometry.hxx" + +using namespace simgear; +using namespace osg; + +typedef std::map > StateSetMap; + +static StateSetMap cloudTextureMap; + +static char vertexShaderSource[] = + "#version 120\n" + "\n" + "varying float fogFactor;\n" + "varying float alphaBlend;\n" + "attribute float textureIndexX;\n" + "attribute float textureIndexY;\n" + "attribute float wScale;\n" + "attribute float hScale;\n" + "attribute float shade;\n" + "void main(void)\n" + "{\n" + " gl_TexCoord[0] = gl_MultiTexCoord0 + vec4(textureIndexX, textureIndexY, 0.0, 0.0);\n" + " vec4 ep = gl_ModelViewMatrixInverse * vec4(0.0,0.0,0.0,1.0);\n" + " vec4 l = gl_ModelViewMatrixInverse * vec4(0.0,0.0,1.0,1.0);\n" + " vec3 u = normalize(ep.xyz - l.xyz);\n" +// Find a rotation matrix that rotates 1,0,0 into u. u, r and w are +// the columns of that matrix. + " vec3 absu = abs(u);\n" + " vec3 r = normalize(vec3(-u.y, u.x, 0));\n" + " vec3 w = cross(u, r);\n" +// Do the matrix multiplication by [ u r w pos]. Assume no +// scaling in the homogeneous component of pos. + " gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n" + " gl_Position.xyz = gl_Vertex.x * u * wScale;\n" + " gl_Position.xyz += gl_Vertex.y * r * hScale;\n" + " gl_Position.xyz += gl_Vertex.z * w;\n" + " gl_Position.xyz += gl_Color.xyz;\n" +// Determine a lighting normal based on the sprites position from the +// center of the cloud. + " float n = dot(normalize(gl_LightSource[0].position.xyz), normalize(mat3x3(gl_ModelViewMatrix) * gl_Position.xyz));\n" +// Determine the position - used for fog and shading calculations + " vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Position);\n" + " float fogCoord = abs(ecPosition.z);\n" +// Final position of the sprite + " gl_Position = gl_ModelViewProjectionMatrix * gl_Position;\n" +// Limit the normal range from [0,1.0], and apply the shading (vertical factor) + " n = min(smoothstep(-0.5, 0.5, n), shade);\n" +// This lighting normal is then used to mix between almost pure ambient (0) and diffuse (1.0) light + " vec4 backlight = 0.8 * gl_LightSource[0].ambient + 0.2 * gl_LightSource[0].diffuse;\n" + " gl_FrontColor = mix(backlight, gl_LightSource[0].diffuse, n);\n" + " gl_FrontColor += gl_FrontLightModelProduct.sceneColor;\n" + " gl_FrontColor.a = smoothstep(10.0, 100.0, fogCoord);\n" + " gl_BackColor = gl_FrontColor;\n" +// Fog doesn't affect clouds as much as other objects. + " fogFactor = exp( -gl_Fog.density * fogCoord);\n" + " fogFactor = clamp(fogFactor, 0.0, 1.0);\n" +// As we get within 100m of the sprite, it is faded out + " alphaBlend = smoothstep(10.0, 100.0, fogCoord);\n" + "}\n"; + +static char fragmentShaderSource[] = + "uniform sampler2D baseTexture; \n" + "varying float fogFactor;\n" + "varying float alphaBlend;\n" + "\n" + "void main(void)\n" + "{\n" + " vec4 base = texture2D( baseTexture, gl_TexCoord[0].st);\n" + " vec4 finalColor = base * gl_Color;\n" +// " finalColor.a = min(alphaBlend, finalColor.a);\n" + " gl_FragColor = mix(gl_Fog.color, finalColor, fogFactor );\n" + "}\n"; + +class SGCloudFogUpdateCallback : public osg::StateAttribute::Callback { + public: + virtual void operator () (osg::StateAttribute* sa, osg::NodeVisitor* nv) + { + SGUpdateVisitor* updateVisitor = static_cast(nv); + osg::Fog* fog = static_cast(sa); + fog->setMode(osg::Fog::EXP); + fog->setColor(updateVisitor->getFogColor().osg()); + fog->setDensity(updateVisitor->getFogExpDensity()); + } +}; + +SGNewCloud::SGNewCloud(const SGPath &tex_path, + string tex, + double min_w, + double max_w, + double min_h, + double max_h, + double min_sprite_w, + double max_sprite_w, + double min_sprite_h, + double max_sprite_h, + double b, + int n, + int nt_x, + int nt_y) : + min_width(min_w), + max_width(max_w), + min_height(min_h), + max_height(max_h), + min_sprite_width(min_sprite_w), + max_sprite_width(max_sprite_w), + min_sprite_height(min_sprite_h), + max_sprite_height(max_sprite_h), + bottom_shade(b), + num_sprites(n), + num_textures_x(nt_x), + num_textures_y(nt_y), + texture(tex) { - init(); - familly = classification; -} - -SGNewCloud::SGNewCloud(string classification) -{ - init(); - if( classification == "cu" ) - familly = CLFamilly_cu; - else if( classification == "cb" ) - familly = CLFamilly_cb; - else if( classification == "st" ) - familly = CLFamilly_st; - else if( classification == "ns" ) - familly = CLFamilly_ns; - else if( classification == "sc" ) - familly = CLFamilly_sc; - else if( classification == "as" ) - familly = CLFamilly_as; - else if( classification == "ac" ) - familly = CLFamilly_ac; - else if( classification == "ci" ) - familly = CLFamilly_ci; - else if( classification == "cc" ) - familly = CLFamilly_cc; - else if( classification == "cs" ) - familly = CLFamilly_cs; + // Create a new StateSet for the texture, if required. + StateSetMap::iterator iter = cloudTextureMap.find(texture); + + if (iter == cloudTextureMap.end()) { + stateSet = new osg::StateSet; + + osg::ref_ptr options = makeOptionsFromPath(tex_path); + + osg::Texture2D *tex = new osg::Texture2D; + tex->setWrap( osg::Texture2D::WRAP_S, osg::Texture2D::CLAMP ); + tex->setWrap( osg::Texture2D::WRAP_T, osg::Texture2D::CLAMP ); + tex->setImage(osgDB::readImageFile(texture, options.get())); + + StateAttributeFactory* attribFactory = StateAttributeFactory::instance(); + + stateSet->setMode(GL_LIGHTING, osg::StateAttribute::ON); + stateSet->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + + // Fog handling + osg::Fog* fog = new osg::Fog; + fog->setUpdateCallback(new SGCloudFogUpdateCallback); + stateSet->setAttributeAndModes(fog); + stateSet->setDataVariance(osg::Object::DYNAMIC); + + stateSet->setAttributeAndModes(attribFactory->getSmoothShadeModel()); + stateSet->setAttributeAndModes(attribFactory->getStandardBlendFunc()); + + stateSet->setTextureAttributeAndModes(0, tex, osg::StateAttribute::ON ); + stateSet->setRenderBinDetails(osg::StateSet::TRANSPARENT_BIN, "DepthSortedBin"); + + static ref_ptr alphaFunc; + static ref_ptr program; + static ref_ptr baseTextureSampler; + static ref_ptr material; + + // Generate the shader etc, if we don't already have one. + if (!program.valid()) { + alphaFunc = new AlphaFunc; + alphaFunc->setFunction(AlphaFunc::GREATER,0.002f); + program = new Program; + baseTextureSampler = new osg::Uniform("baseTexture", 0); + Shader* vertex_shader = new Shader(Shader::VERTEX, vertexShaderSource); + program->addShader(vertex_shader); + program->addBindAttribLocation("textureIndexX", CloudShaderGeometry::TEXTURE_INDEX_X); + program->addBindAttribLocation("textureIndexY", CloudShaderGeometry::TEXTURE_INDEX_Y); + program->addBindAttribLocation("wScale", CloudShaderGeometry::WIDTH); + program->addBindAttribLocation("hScale", CloudShaderGeometry::HEIGHT); + program->addBindAttribLocation("shade", CloudShaderGeometry::SHADE); + + Shader* fragment_shader = new Shader(Shader::FRAGMENT, fragmentShaderSource); + program->addShader(fragment_shader); + material = new Material; + // Don´t track vertex color + material->setColorMode(Material::OFF); + + // We don't actually use the material information either - see shader. + material->setAmbient(Material::FRONT_AND_BACK, + Vec4(0.5f, 0.5f, 0.5f, 1.0f)); + material->setDiffuse(Material::FRONT_AND_BACK, + Vec4(0.5f, 0.5f, 0.5f, 1.0f)); + } + + stateSet->setAttributeAndModes(alphaFunc.get()); + stateSet->setAttribute(program.get()); + stateSet->addUniform(baseTextureSampler.get()); + stateSet->setMode(GL_VERTEX_PROGRAM_TWO_SIDE, StateAttribute::ON); + stateSet->setAttribute(material.get()); + + // Add the newly created texture to the map for use later. + cloudTextureMap.insert(StateSetMap::value_type(texture, stateSet)); + } else { + stateSet = iter->second.get(); + } } 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.png"); - // OSGFIXME -// cloudTextures[ CLTexture_cumulus ] = new osg::Texture2D( cloud_path.str().c_str(), false, false, false ); - cloudTextures[ CLTexture_cumulus ] = new osg::Texture2D; - - cloud_path.set(tex_path); - cloud_path.append("cl_stratus.png"); - // OSGFIXME -// cloudTextures[ CLTexture_stratus ] = new ssgTexture( cloud_path.str().c_str(), false, false, false ); - cloudTextures[ CLTexture_stratus ] = new osg::Texture2D; - -} - -void SGNewCloud::startFade(bool direction, float duration, float pauseLength) { - if(duration <= 0.0) { - fadeActive = false; - return; - } - this->direction = direction; - fadetimer = 0.0; - this->duration = duration; - this->pauseLength = pauseLength; - last_step = -1.0; - fadeActive = true; -} -void SGNewCloud::setFade(float howMuch) { - duration = 100.0; - fadetimer = howMuch; - fadeActive = false; - last_step = -1.0; -} - - -static inline 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(sqrt (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 inline void lightFunction(sgVec3 normal, sgVec4 light, float pf) { - float cosAngle = sgScalarProductVec3( normal, SGNewCloud::modelSunDir); - float vl = (1.0f - 0.5f + cosAngle * 0.5f) * pf; - sgScaleVec3( light, SGNewCloud::sunlight, 0.25f + 0.75f * 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 -void SGNewCloud::computeSimpleLight(sgVec3 FakeEyePos) { - // constant Rayleigh factor if we are not doing Anisotropic lighting - float pf = 1.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); - } - lightFunction(iSprite->n0, iSprite->l0, pf); - lightFunction(iSprite->n1, iSprite->l1, pf); - lightFunction(iSprite->n2, iSprite->l2, pf); - lightFunction(iSprite->n3, 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 ); - // don't place cloud below his base - if( y - r*0.50 < delta_base ) - delta_base = y - r*0.50; -} - -// 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 - delta_base, 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.70f; // 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; - +osg::Geometry* createOrthQuad(float w, float h, int varieties_x, int varieties_y) +{ + // Create front and back polygons so we don't need to screw around + // with two-sided lighting in the shader. + osg::Vec3Array& v = *(new osg::Vec3Array(4)); + osg::Vec3Array& n = *(new osg::Vec3Array(4)); + osg::Vec2Array& t = *(new osg::Vec2Array(4)); + + float cw = w*0.5f; + + v[0].set(0.0f, -cw, 0.0f); + v[1].set(0.0f, cw, 0.0f); + v[2].set(0.0f, cw, h); + v[3].set(0.0f, -cw, h); + + // The texture coordinate range is not the + // entire coordinate space - as the texture + // has a number of different clouds on it. + float tx = 1.0f/varieties_x; + float ty = 1.0f/varieties_y; + + t[0].set(0.0f, 0.0f); + t[1].set( tx, 0.0f); + t[2].set( tx, ty); + t[3].set(0.0f, ty); + + // The normal isn't actually use in lighting. + n[0].set(1.0f, -1.0f, -1.0f); + n[1].set(1.0f, 1.0f, -1.0f); + n[2].set(1.0f, 1.0f, 1.0f); + n[3].set(1.0f, -1.0f, 1.0f); + + osg::Geometry *geom = new osg::Geometry; + + geom->setVertexArray(&v); + geom->setTexCoordArray(0, &t); + geom->setNormalArray(&n); + geom->setNormalBinding(Geometry::BIND_PER_VERTEX); + // No color for now; that's used to pass the position. + geom->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::QUADS,0,4)); + + return geom; } -// return a random number between -n/2 and n/2 +// return a random number between -n/2 and n/2, tending to 0 static float Rnd(float n) { - return n * (-0.5f + sg_random()); -} - -// generate all sprite with defined boxes -void SGNewCloud::genSprites( void ) { - float x, y, z, r; - int N, sc; - minx = miny = minz = 99999.0; - maxx = maxy = maxz = -99999.0; - - 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: - sc = 1; - 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.75f); - z = thisBox->pos[SG_Z] + Rnd(thisBox->r * 0.75f); - 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 ); - - const float ang = 45.0f * SG_PI / 180.0f; - - // compute normals - list_of_spriteDef::iterator iSprite; - for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) { - sgVec3 normal; - spriteContainer *thisSpriteContainer = &list_spriteContainer[iSprite->box]; - if( familly == CLFamilly_sc || familly == CLFamilly_cu || familly == CLFamilly_cb) { - sgSubVec3(normal, iSprite->pos, center); - } else { - sgSubVec3(normal, iSprite->pos, thisSpriteContainer->pos); - sgSubVec3(normal, thisSpriteContainer->center); - sgSubVec3(normal, cloudpos); - } - if( normal[0] == 0.0f && normal[1] == 0.0f && normal[2] == 0.0f ) - sgSetVec3( normal, 0.0f, 1.0f, 0.0f ); - sgNormaliseVec3(normal); - // 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, pt; - // I suspect this code to be bugged... - CartToPolar3d(normal, polar); - sgCopyVec3(iSprite->normal, normal); - - // offset the normal vector by some angle for each vertex - sgSetVec3(pt, polar[0] - ang, polar[1] - ang, polar[2]); - PolarToCart3d(pt, iSprite->n0); - sgSetVec3(pt, polar[0] + ang, polar[1] - ang, polar[2]); - PolarToCart3d(pt, iSprite->n1); - sgSetVec3(pt, polar[0] + ang, polar[1] + ang, polar[2]); - PolarToCart3d(pt, iSprite->n2); - sgSetVec3(pt, polar[0] - ang, polar[1] + ang, polar[2]); - PolarToCart3d(pt, iSprite->n3); - } - - // experimental : clouds are dissipating with time - if( familly == CLFamilly_cu ) { - startFade(true, 300.0f, 30.0f); - fadetimer = sg_random() * 300.0f; - } -} - - -// definition of a cu cloud, only for testing -void SGNewCloud::new_cu(void) { - float s = 250.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 = 675.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]); -} - - - - -void SGNewCloud::drawContainers() { - - -} - - - -// 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]); - } - std::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 ) { - - float step = ( list_spriteDef.size() * (direction ? fadetimer : duration-fadetimer)) / duration; - int clrank = (int) step; - float clfadeinrank = (step - clrank); - last_step = step; - - float CloudVisFade = 1.0 / (0.7f * SGCloudField::get_CloudVis()); - // blend clouds with sky based on distance to limit the contrast of distant cloud - float t = 1.0f - dist_center * CloudVisFade; -// if ( t < 0.0f ) -// return; - - computeSimpleLight( FakeEyePos ); - - // view point sort, we sort because of transparency - sortSprite( FakeEyePos ); - - float dark = (familly == CLFamilly_cb ? 0.9f : 1.0f); - - GLint previousTexture = -1, thisTexture; - list_of_spriteDef::iterator iSprite; - for( iSprite = list_spriteDef.begin() ; iSprite != list_spriteDef.end() ; iSprite++ ) { - // skip this sprite if faded - if(iSprite->rank > clrank) - continue; - // 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; - // OSGFIXME -// glBindTexture(GL_TEXTURE_2D, cloudTextures[thisTexture]->getHandle()); - } - - sgVec3 translate; - 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 ) - 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.8f * dark); - sgScaleVec3(iSprite->l1, 0.8f * dark); - sgScaleVec3(iSprite->l2, dark); - sgScaleVec3(iSprite->l3, dark); - break; - case CLbox_stratus: - // usually dark grey - if( familly == CLFamilly_st ) { - sgScaleVec3(iSprite->l0, 0.8f); - sgScaleVec3(iSprite->l1, 0.8f); - sgScaleVec3(iSprite->l2, 0.8f); - sgScaleVec3(iSprite->l3, 0.8f); - } else { - sgScaleVec3(iSprite->l0, 0.7f); - sgScaleVec3(iSprite->l1, 0.7f); - sgScaleVec3(iSprite->l2, 0.7f); - sgScaleVec3(iSprite->l3, 0.7f); - } - 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 ) { - // now clouds at the far plane are half blended - sgScaleVec4( l0, t ); - sgScaleVec4( l1, t ); - sgScaleVec4( l2, t ); - sgScaleVec4( l3, t ); - } - if( iSprite->rank == clrank ) { - sgScaleVec4( l0, clfadeinrank ); - sgScaleVec4( l1, clfadeinrank ); - sgScaleVec4( l2, clfadeinrank ); - sgScaleVec4( l3, clfadeinrank ); - } - // 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 ); - translate[2] -= FakeEyePos[1]; -// sgNormaliseVec3( translate ); - float dist_sprite = sgLengthVec3 ( translate ); - sgScaleVec3 ( translate, SG_ONE / dist_sprite ) ; - sgVec3 x, y, up = {0.0f, 0.0f, 1.0f}; - if( dist_sprite > 2*r ) { - sgVectorProductVec3(x, translate, up); - sgVectorProductVec3(y, x, translate); - } else { - sgCopyVec3( x, SGCloudField::view_X ); - sgCopyVec3( y, SGCloudField::view_Y ); - } - sgScaleVec3(x, r); - 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, bool first_time, float dist_center) { - - sgVec3 translate; - sgSubVec3( translate, center, deltaPos); - - // blend clouds with sky based on distance to limit the contrast of distant cloud - float CloudVisFade = (1.0f * SGCloudField::get_CloudVis()); - - float t = 1.0f - (dist_center - 1.0*radius) / CloudVisFade; - if ( t < 0.0f ) - return; - if( t > 1.0f ) - t = 1.0f; - if( t > 0.50f ) - t *= 1.1f; - glColor4f(t, t, t, t); - float r = radius; - // 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 ); - pos[2] += deltaPos[1]; - - sgNormaliseVec3( translate ); - sgVec3 x, y, up = {0.0f, 0.0f, 1.0f}; - sgVectorProductVec3(x, translate, up); - sgVectorProductVec3(y, x, translate); - if(first_time) { - sgCopyVec3( rotX, x ); - sgCopyVec3( rotY, y ); - } else if(fabs(sgScalarProductVec3(rotX, x)) < 0.93f || fabs(sgScalarProductVec3(rotY, y)) < 0.93f ) { - // ask for a redraw of this impostor if the view angle changed too much - sgCopyVec3( rotX, x ); - sgCopyVec3( rotY, y ); - cldCache->invalidate(cloudId, bbId); - } - sgScaleVec3(x, r); - sgScaleVec3(y, r); - - sgVec3 left, right; - sgCopyVec3( left, pos ); - sgSubVec3 (left, y); - sgAddVec3 (right, left, x); - sgSubVec3 (left, x); - - glBegin(GL_QUADS); - glTexCoord2f(0.0f, 0.0f); - glVertex3fv(left); - glTexCoord2f(1.0f, 0.0f); - glVertex3fv(right); - sgScaleVec3( y, 2.0 ); - sgAddVec3( left, y); - sgAddVec3( right, y); - glTexCoord2f(1.0f, 1.0f); - glVertex3fv(right); - glTexCoord2f(0.0f, 1.0f); - glVertex3fv(left); - glEnd(); - -#if 0 // 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 - -} - -// 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 true; + return n * (-0.5f + (sg_random() + sg_random()) / 2.0f); } - - -// render the cloud, fakepos is a relative position inside the cloud field -void SGNewCloud::Render(sgVec3 FakeEyePos) { - sgVec3 dist; - - sgVec3 deltaPos; - sgCopyVec3( deltaPos, FakeEyePos); - deltaPos[1] = 0.0; - sgSubVec3( dist, center, FakeEyePos); - float dist_center = sgLengthVec3(dist); - - if( fadeActive ) { - fadetimer += SGCloudField::timer_dt; - if( fadetimer > duration + pauseLength ) { - // fade out after fade in, and vice versa - direction = ! direction; - fadetimer = 0.0; - } - } - - if( !isBillboardable(dist_center) ) { - // not a good candidate for impostors, draw a real cloud - Render3Dcloud(false, FakeEyePos, deltaPos, dist_center); - } else { - GLuint texID = 0; - bool first_time = false; - // 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); - first_time = true; - } - if( texID == 0 ) { - // no more free texture in the pool - Render3Dcloud(false, FakeEyePos, deltaPos, dist_center); - } else { - float angleX=0.0f, angleY=0.0f; - - // force a redraw of the impostor if the cloud shape has changed enought - float step = ( list_spriteDef.size() * (direction ? fadetimer : duration-fadetimer)) / duration; - if( fabs(step - last_step) > 0.5f ) - cldCache->invalidate(cloudId, bbId); - - 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(FakeEyePos, first_time, dist_center); - } - } - +osg::ref_ptr SGNewCloud::genCloud() { + LOD* result = new LOD; + Geode* geode = new Geode; + CloudShaderGeometry* sg = new CloudShaderGeometry(num_textures_x, num_textures_y); + + Geometry* quad = createOrthQuad(min_sprite_width, min_sprite_height, num_textures_x, num_textures_y); + + // Determine how big this specific cloud instance is. + float width = min_width + sg_random() * (max_width - min_width); + float height = min_height + sg_random() * (max_height - min_height); + + // Determine the cull distance. This is used to remove sprites that are too close together. + // The value is squared as we use vector calculations. + float cull_distance_squared = min_sprite_height * min_sprite_height * 0.1f; + + for (int i = 0; i < num_sprites; i++) + { + // Determine the position of the sprite. + SGVec3f *pos = new SGVec3f(Rnd(width), + Rnd(width), + Rnd(height)); + + // Determine the height and width as scaling factors on the minimum size (used to create the quad) + float sprite_width = 1.0f + sg_random() * (max_sprite_width - min_sprite_width) / min_sprite_width; + float sprite_height = 1.0f + sg_random() * (max_sprite_height - min_sprite_height) / min_sprite_height; + + // The shade varies from bottom_shade to 1.0 non-linearly + float shade; + if (pos->z() > 0.0f) { + shade = 1.0f; + } else { + shade = ((2 * pos->z() + height) / height) * (1 - bottom_shade) + bottom_shade; + } + + // Determine the sprite texture indexes; + int index_x = (int) floor(sg_random() * num_textures_x); + if (index_x == num_textures_x) { index_x--; } + + int index_y = (int) floor(sg_random() * num_textures_y); + if (index_y == num_textures_y) { index_y--; } + + sg->addSprite(*pos, index_x, index_y, sprite_width, sprite_height, shade, cull_distance_squared); + } + + sg->setGeometry(quad); + geode->addDrawable(sg); + geode->setName("3D cloud"); + geode->setStateSet(stateSet.get()); + result->addChild(geode, 0, 20000); + return result; } - diff --git a/simgear/scene/sky/newcloud.hxx b/simgear/scene/sky/newcloud.hxx index b3a2846f..3cb93dd4 100644 --- a/simgear/scene/sky/newcloud.hxx +++ b/simgear/scene/sky/newcloud.hxx @@ -27,6 +27,7 @@ #include #include #include +#include #include "bbcache.hxx" @@ -39,137 +40,47 @@ using std::vector; class SGNewCloud { public: - enum CLFamilly_type { - CLFamilly_cu = 0, - CLFamilly_cb, - CLFamilly_st, - CLFamilly_ns, - CLFamilly_sc, - CLFamilly_as, - CLFamilly_ac, - CLFamilly_ci, - CLFamilly_cc, - CLFamilly_cs, - CLFamilly_nn - }; - SGNewCloud(CLFamilly_type classification=CLFamilly_nn); - SGNewCloud(string classification); + SGNewCloud(const SGPath &tex_path, + string tex, + double min_w, + double max_w, + double min_h, + double max_h, + double min_sprite_w, + double max_sprite_w, + double min_sprite_h, + double max_sprite_h, + double b, + int n, + int nt_x, + int nt_y); + ~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 - }; + // Generate a Cloud + osg::ref_ptr genCloud (); private: - class spriteDef { - public: - sgVec3 pos; - float r; - CLbox_type sprite_type; - sgVec4 l0, l1, l2, l3; - sgVec3 normal, n0, n1, n2, n3; - 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 init(void); - - 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, bool first_time, 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; - sgVec3 rotX, rotY; - -// int rank; - sgVec3 cloudpos, center; - float delta_base; - list_of_spriteDef list_spriteDef; - list_of_spriteContainer list_spriteContainer; - float radius; - CLFamilly_type familly; - - // fading data - bool direction, fadeActive; - float duration, pauseLength, fadetimer; - float last_step; + double min_width; + double max_width; + double min_height; + double max_height; + double min_sprite_width; + double max_sprite_width; + double min_sprite_height; + double max_sprite_height; + double bottom_shade; + int num_sprites; + int num_textures_x; + int num_textures_y; + const string texture; + + osg::ref_ptr stateSet; 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; } - inline sgVec3 *getCenter() { return ¢er; } - inline int getId() { return cloudId; } - - inline CLFamilly_type getFamilly(void) { return familly; } - - // 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 diff --git a/simgear/scene/sky/sky.cxx b/simgear/scene/sky/sky.cxx index ae8cd88b..4b6e4743 100644 --- a/simgear/scene/sky/sky.cxx +++ b/simgear/scene/sky/sky.cxx @@ -30,6 +30,7 @@ #include "sky.hxx" #include "cloudfield.hxx" +#include "newcloud.hxx" // Constructor SGSky::SGSky( void ) { @@ -43,6 +44,9 @@ SGSky::SGSky( void ) { ramp_down = 0.15; in_cloud = -1; + + clouds_3d_enabled = false; + clouds_3d_density = 0.8; pre_root = new osg::Group; pre_root->setNodeMask(simgear::BACKGROUND_BIT); @@ -85,7 +89,7 @@ void SGSky::build( double h_radius_m, double v_radius_m, pre_selector->addChild( pre_transform.get() ); - pre_root->addChild( pre_selector.get() ); + pre_root->addChild( pre_selector.get() ); } @@ -161,6 +165,8 @@ SGSky::add_cloud_layer( SGCloudLayer * layer ) { cloud_layers.push_back(layer); cloud_root->addChild(layer->getNode()); + + layer->set_enable3dClouds(clouds_3d_enabled); } const SGCloudLayer * @@ -181,6 +187,36 @@ SGSky::get_cloud_layer_count () const return cloud_layers.size(); } +void SGSky::set_3dClouds(bool enable) +{ + for ( unsigned i = 0; i < cloud_layers.size(); ++i ) { + cloud_layers[i]->set_enable3dClouds(enable); + } + + clouds_3d_enabled = enable; +} + +bool SGSky::get_3dClouds() const { + return clouds_3d_enabled; +} + +double SGSky::get_3dCloudDensity() const { + return SGCloudField::get_density(); +} + +void SGSky::set_3dCloudDensity(double density) +{ + SGCloudField::set_density(density); + + for ( unsigned i = 0; i < cloud_layers.size(); ++i ) { + cloud_layers[i]->applyDensity(); + } +} + +void SGSky::texture_path( const string& path ) { + tex_path = SGPath( path ); +} + // modify the current visibility based on cloud layers, thickness, // transition range, and simulated "puffs". void SGSky::modify_vis( float alt, float time_factor ) { @@ -214,7 +250,7 @@ void SGSky::modify_vis( float alt, float time_factor ) { } if ( cloud_layers[i]->getCoverage() == SGCloudLayer::SG_CLOUD_CLEAR || - cloud_layers[i]->get_layer3D()->is3D() && SGCloudField::enable3D) { + get_3dClouds()) { // do nothing, clear layers aren't drawn, don't affect // visibility andn dont' need to be faded in or out. } else if ( (cloud_layers[i]->getCoverage() == @@ -311,3 +347,5 @@ void SGSky::modify_vis( float alt, float time_factor ) { effective_visibility = effvis; } + + diff --git a/simgear/scene/sky/sky.hxx b/simgear/scene/sky/sky.hxx index 46c30284..c7308784 100644 --- a/simgear/scene/sky/sky.hxx +++ b/simgear/scene/sky/sky.hxx @@ -237,6 +237,12 @@ private: double ramp_up; // in seconds double ramp_down; // in seconds + // 3D clouds enabled + bool clouds_3d_enabled; + + // 3D cloud density + double clouds_3d_density; + public: /** Constructor */ @@ -338,9 +344,8 @@ public: * Specify the texture path (optional, defaults to current directory) * @param path base path to texture locations */ - inline void texture_path( const string& path ) { - tex_path = SGPath( path ); - } + void texture_path( const string& path ); + /** Enable drawing of the sky. */ inline void enable() { pre_selector->setValue(0, 1); @@ -408,6 +413,23 @@ public: inline void set_visibility( float v ) { effective_visibility = visibility = (v <= 25.0) ? 25.0 : v; } + + /** Get whether 3D clouds are enabled */ + virtual bool get_3dClouds() const; + + /** Set whether 3D clouds are enabled + * @param enable Whether to enable 3D clouds + */ + virtual void set_3dClouds(bool enable); + + /** Get 3D cloud density */ + virtual double get_3dCloudDensity() const; + + /** Set 3D cloud density + * @param density 3D cloud density + */ + virtual void set_3dCloudDensity(double density); + }; -- 2.39.5