diff --git a/src/Environment/Makefile.am b/src/Environment/Makefile.am index a16996170..3661e5041 100644 --- a/src/Environment/Makefile.am +++ b/src/Environment/Makefile.am @@ -8,6 +8,6 @@ libEnvironment_a_SOURCES = \ environment.cxx environment.hxx \ environment_mgr.cxx environment_mgr.hxx \ environment_ctrl.cxx environment_ctrl.hxx \ - fgmetar.cxx fgmetar.hxx + fgmetar.cxx fgmetar.hxx fgclouds.cxx fgclouds.hxx INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/src/Environment/environment.cxx b/src/Environment/environment.cxx index 2448ec3ed..6a607bba0 100644 --- a/src/Environment/environment.cxx +++ b/src/Environment/environment.cxx @@ -36,6 +36,7 @@ #include #include #include +#include #include
#include "environment.hxx" @@ -297,6 +298,9 @@ FGEnvironment::get_wind_from_down_fps () const double FGEnvironment::get_turbulence_magnitude_norm () const { + if( sgEnviro.get_turbulence_enable_state() ) + if (fgGetBool("/environment/params/real-world-weather-fetch") == true) + return sgEnviro.get_cloud_turbulence(); return turbulence_magnitude_norm; } diff --git a/src/Environment/environment_ctrl.cxx b/src/Environment/environment_ctrl.cxx index 4a7b24732..9872c6d0c 100644 --- a/src/Environment/environment_ctrl.cxx +++ b/src/Environment/environment_ctrl.cxx @@ -494,6 +494,7 @@ FGMetarEnvironmentCtrl::update(double delta_time_sec) result_queue.pop(); if ( result.m != NULL ) { update_metar_properties( result.m ); + fgSetString("/environment/metar/last-metar", result.m->getData()); delete result.m; update_env_config(); env->reinit(); diff --git a/src/Environment/environment_mgr.cxx b/src/Environment/environment_mgr.cxx index 9dc036242..dc56ec0e8 100644 --- a/src/Environment/environment_mgr.cxx +++ b/src/Environment/environment_mgr.cxx @@ -31,7 +31,7 @@ #include "environment.hxx" #include "environment_ctrl.hxx" #include "environment_mgr.hxx" - +#include "fgclouds.hxx" class SGSky; extern SGSky *thesky; @@ -49,12 +49,14 @@ FGEnvironmentMgr::FGEnvironmentMgr () _controller->setEnvironment(_environment); set_subsystem("controller", _controller, 0.5); + fgClouds = new FGClouds; } FGEnvironmentMgr::~FGEnvironmentMgr () { delete _environment; delete _controller; + delete fgClouds; } void @@ -171,6 +173,15 @@ FGEnvironmentMgr::bind () fgTie("/sim/rendering/precipitation-enable", &sgEnviro, &SGEnviro::get_precipitation_enable_state, &SGEnviro::set_precipitation_enable_state); + fgTie("/environment/rebuild_layers", fgClouds, + &FGClouds::get_update_event, + &FGClouds::set_update_event); + fgTie("/sim/rendering/lightning-enable", &sgEnviro, + &SGEnviro::get_lightning_enable_state, + &SGEnviro::set_lightning_enable_state); + fgTie("/environment/turbulence/use-cloud-turbulence", &sgEnviro, + &SGEnviro::get_turbulence_enable_state, + &SGEnviro::set_turbulence_enable_state); } void @@ -200,6 +211,15 @@ FGEnvironmentMgr::unbind () sprintf(buf, "/environment/clouds/layer[%d]/type", i); fgUntie(buf); } + fgUntie("/sim/rendering/clouds3d-enable"); + fgUntie("/sim/rendering/clouds3d-vis-range"); + fgUntie("/sim/rendering/clouds3d-density"); + fgUntie("/sim/rendering/clouds3d-cache-size"); + fgUntie("/sim/rendering/clouds3d-cache-resolution"); + fgUntie("/sim/rendering/precipitation-enable"); + fgUntie("/environment/rebuild_layers"); + fgUntie("/sim/rendering/lightning-enable"); + fgUntie("/environment/turbulence/use-cloud-turbulence"); } void diff --git a/src/Environment/environment_mgr.hxx b/src/Environment/environment_mgr.hxx index abef848f5..ff0111afe 100644 --- a/src/Environment/environment_mgr.hxx +++ b/src/Environment/environment_mgr.hxx @@ -34,7 +34,7 @@ class FGEnvironment; class FGEnvironmentCtrl; - +class FGClouds; /** * Manage environment information. @@ -87,6 +87,7 @@ private: FGEnvironment * _environment; // always the same, for now FGEnvironmentCtrl * _controller; // always the same, for now + FGClouds *fgClouds; }; #endif // _ENVIRONMENT_MGR_HXX diff --git a/src/Environment/fgclouds.cxx b/src/Environment/fgclouds.cxx new file mode 100644 index 000000000..022a1a4bc --- /dev/null +++ b/src/Environment/fgclouds.cxx @@ -0,0 +1,442 @@ +// Build a cloud layer based on metar +// +// Written by Harald JOHNSEN, started April 2005. +// +// Copyright (C) 2005 Harald JOHNSEN - hjohnsen@evc.net +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA +// +// + +#include
+ +#include +#include +#include +#include +#include +#include +#include +#include
+#include +#include
+ +#include "environment_mgr.hxx" +#include "fgmetar.hxx" +#include "fgclouds.hxx" + +extern SGSky *thesky; + + +FGClouds::FGClouds() : + station_elevation_ft(0.0), + snd_lightning(NULL) +{ + update_event = 0; + fgSetString("/environment/weather-scenario", "METAR"); +} +FGClouds::~FGClouds() { +} + +int FGClouds::get_update_event(void) const { + return update_event; +} +void FGClouds::set_update_event(int count) { + update_event = count; + build(); +} + +void FGClouds::init(void) { + if( snd_lightning == NULL ) { + snd_lightning = new SGSoundSample(globals->get_fg_root().c_str(), "Sounds/thunder.wav", true); + snd_lightning->set_max_dist(7000.0f); + snd_lightning->set_reference_dist(3000.0f); + SGSoundMgr *soundMgr = globals->get_soundmgr(); + soundMgr->add( snd_lightning, "thunder" ); + sgEnviro.set_soundMgr( soundMgr ); + } +} + +SGNewCloud *FGClouds::buildCloud(SGPropertyNode *cloud_def_root, string name) { + SGPropertyNode *cld_def=NULL; + + cld_def = cloud_def_root->getChild(name.c_str()); + string base_name = name.substr(0,2); + if( !cld_def ) { + if( name[2] == '-' ) { + cld_def = cloud_def_root->getChild(base_name.c_str()); + } + if( !cld_def ) + return NULL; + } + string familly = cld_def->getStringValue("familly", base_name.c_str()); + SGNewCloud *cld = new SGNewCloud(familly); + for(int i = 0; i < cld_def->nChildren() ; i++) { + SGPropertyNode *abox = cld_def->getChild(i); + if( strcmp(abox->getName(), "box") == 0) { + double x = abox->getDoubleValue("x"); + double y = abox->getDoubleValue("y"); + double z = abox->getDoubleValue("z"); + double size = abox->getDoubleValue("size"); + int type = abox->getIntValue("type", SGNewCloud::CLbox_standard); + cld->addContainer(x, y, z, size, (SGNewCloud::CLbox_type) type); + } + } + cld->genSprites(); + return cld; +} + +void FGClouds::buildLayer(SGCloudField *layer, string name, double alt, double coverage) { + struct { + string name; + double count; + } tCloudVariety[20]; + int CloudVarietyCount = 0; + double totalCount = 0.0; + + SGPropertyNode *cloud_def_root = fgGetNode("/environment/config/cloudlayers/clouds", false); + SGPropertyNode *layer_def_root = fgGetNode("/environment/config/cloudlayers/layers", false); + + layer->clear(); + // when we don't generate clouds the layer is rendered in 2D + if( coverage == 0.0 ) + return; + if( layer_def_root == NULL || cloud_def_root == NULL) + return; + if( name == "ci" || name == "sc" || name == "st") + return; + + SGPropertyNode *layer_def=NULL; + + layer_def = layer_def_root->getChild(name.c_str()); + if( !layer_def ) { + if( name[2] == '-' ) { + string base_name = name.substr(0,2); + layer_def = layer_def_root->getChild(base_name.c_str()); + } + if( !layer_def ) + return; + } + + double grid_x_size = layer_def->getDoubleValue("grid-x-size", 1000.0); + double grid_y_size = layer_def->getDoubleValue("grid-y-size", 1000.0); + double grid_x_rand = layer_def->getDoubleValue("grid-x-rand", grid_x_size); + double grid_y_rand = layer_def->getDoubleValue("grid-y-rand", grid_y_size); + double grid_z_rand = layer_def->getDoubleValue("grid-z-rand"); + + for(int i = 0; i < layer_def->nChildren() ; i++) { + SGPropertyNode *acloud = layer_def->getChild(i); + if( strcmp(acloud->getName(), "cloud") == 0) { + string cloud_name = acloud->getStringValue("name"); + tCloudVariety[CloudVarietyCount].name = cloud_name; + double count = acloud->getDoubleValue("count", 1.0); + tCloudVariety[CloudVarietyCount].count = count; + int variety = 0; + cloud_name = cloud_name + "-%d"; + char variety_name[50]; + do { + variety++; + snprintf(variety_name, sizeof(variety_name), cloud_name.c_str(), variety); + } while( cloud_def_root->getChild(variety_name, 0, false) ); + + totalCount += count; + if( CloudVarietyCount < 20 ) + CloudVarietyCount++; + } + } + totalCount = 1.0 / totalCount; + double currCoverage = 0.0; + + for(double px = 0.0; px < SGCloudField::fieldSize; px += grid_x_size) { + for(double py = 0.0; py < SGCloudField::fieldSize; py += grid_y_size) { + double x = px + grid_x_rand * (sg_random() - 0.5); + double y = py + grid_y_rand * (sg_random() - 0.5); + double z = alt + grid_z_rand * (sg_random() - 0.5); + double choice = sg_random(); + currCoverage += coverage; + if( currCoverage < 1.0 ) + continue; + currCoverage -= 1.0; + + for(int i = 0; i < CloudVarietyCount ; i ++) { + choice -= tCloudVariety[i].count * totalCount; + if( choice <= 0.0 ) { + SGNewCloud *cld = buildCloud(cloud_def_root, tCloudVariety[i].name); + sgVec3 pos={x,z,y}; + if( cld ) + layer->addCloud(pos, cld); + + break; + } + } + } + } + +} + +// TODO:call this after real metar updates +void FGClouds::buildMETAR(void) { + SGPropertyNode *metar_root = fgGetNode("/environment", true); + + double wind_speed_kt = metar_root->getDoubleValue("wind-speed-kt"); + double temperature_degc = metar_root->getDoubleValue("temperature-sea-level-degc"); + double dewpoint_degc = metar_root->getDoubleValue("dewpoint-sea-level-degc"); + double pressure_mb = metar_root->getDoubleValue("pressure-sea-level-inhg") * SG_INHG_TO_PA / 100.0; + + double dewp = pow(10.0, 7.5 * dewpoint_degc / (237.7 + dewpoint_degc)); + double temp = pow(10.0, 7.5 * temperature_degc / (237.7 + temperature_degc)); + double rel_humidity = dewp * 100 / temp; + + // formule d'Epsy, base d'un cumulus + double cumulus_base = 122.0 * (temperature_degc - dewpoint_degc); + double stratus_base = 100.0 * (100.0 - rel_humidity) * SG_FEET_TO_METER; + + bool cu_seen = false; + + for(int iLayer = 0 ; iLayer < thesky->get_cloud_layer_count(); iLayer++) { + SGPropertyNode *cloud_root = fgGetNode("/environment/clouds/layer", iLayer, true); + + double alt_ft = cloud_root->getDoubleValue("elevation-ft"); + double alt_m = alt_ft * SG_FEET_TO_METER; + string coverage = cloud_root->getStringValue("coverage"); + double coverage_norm = 0.0; + if( coverage == "few" ) + coverage_norm = 2.0/8.0; // <1-2 + else if( coverage == "scattered" ) + coverage_norm = 4.0/8.0; // 3-4 + else if( coverage == "broken" ) + coverage_norm = 6.0/8.0; // 5-7 + else if( coverage == "overcast" ) + coverage_norm = 8.0/8.0; // 8 + + string layer_type = "nn"; + if( coverage == "cirrus" ) { + layer_type = "ci"; + } else if( alt_ft > 16500 ) { +// layer_type = "ci|cs|cc"; + layer_type = "ci"; + } else if( alt_ft > 6500 ) { +// layer_type = "as|ac|ns"; + layer_type = "ac"; + if( pressure_mb < 1005.0 && coverage_norm >= 5.5 ) + layer_type = "ns"; + } else { +// layer_type = "st|cu|cb|sc"; + // +/- 20% from stratus probable base + if( stratus_base * 0.80 < alt_m && stratus_base * 1.40 > alt_m ) + layer_type = "st"; + // +/- 20% from cumulus probable base + else if( cumulus_base * 0.80 < alt_m && cumulus_base * 1.20 > alt_m ) + layer_type = "cu"; + else { + // above formulae is far from perfect + if ( alt_ft < 2000 ) + layer_type = "st"; + else if( alt_ft < 4500 ) + layer_type = "cu"; + else + layer_type = "sc"; + } + } + + SGCloudField *layer3D = thesky->get_cloud_layer(iLayer)->get_layer3D(); + buildLayer(layer3D, layer_type, alt_m, coverage_norm); + } +} + +// copy from FGMetarEnvironmentCtrl until better +void +FGClouds::update_metar_properties( FGMetar *m ) +{ + int i; + double d; + char s[128]; + + fgSetString("/environment/metar/station-id", m->getId()); + fgSetDouble("/environment/metar/min-visibility-m", + m->getMinVisibility().getVisibility_m() ); + fgSetDouble("/environment/metar/max-visibility-m", + m->getMaxVisibility().getVisibility_m() ); + + SGMetarVisibility *dirvis = m->getDirVisibility(); + for (i = 0; i < 8; i++, dirvis++) { + const char *min = "/environment/metar/visibility[%d]/min-m"; + const char *max = "/environment/metar/visibility[%d]/max-m"; + + d = dirvis->getVisibility_m(); + + snprintf(s, 128, min, i); + fgSetDouble(s, d); + snprintf(s, 128, max, i); + fgSetDouble(s, d); + } + + fgSetInt("/environment/metar/base-wind-range-from", + m->getWindRangeFrom() ); + fgSetInt("/environment/metar/base-wind-range-to", + m->getWindRangeTo() ); + fgSetDouble("/environment/metar/base-wind-speed-kt", + m->getWindSpeed_kt() ); + fgSetDouble("/environment/metar/gust-wind-speed-kt", + m->getGustSpeed_kt() ); + fgSetDouble("/environment/metar/temperature-degc", + m->getTemperature_C() ); + fgSetDouble("/environment/metar/dewpoint-degc", + m->getDewpoint_C() ); + fgSetDouble("/environment/metar/rel-humidity-norm", + m->getRelHumidity() ); + fgSetDouble("/environment/metar/pressure-inhg", + m->getPressure_inHg() ); + + vector cv = m->getClouds(); + vector::iterator cloud; + + const char *cl = "/environment/clouds/layer[%i]"; + for (i = 0, cloud = cv.begin(); cloud != cv.end(); cloud++, i++) { + const char *coverage_string[5] = + { "clear", "few", "scattered", "broken", "overcast" }; + const double thickness[5] = { 0, 65, 600,750, 1000}; + int q; + + snprintf(s, 128, cl, i); + strncat(s, "/coverage", 128); + q = cloud->getCoverage(); + fgSetString(s, coverage_string[q] ); + + snprintf(s, 128, cl, i); + strncat(s, "/elevation-ft", 128); + fgSetDouble(s, cloud->getAltitude_ft() + station_elevation_ft); + + snprintf(s, 128, cl, i); + strncat(s, "/thickness-ft", 128); + fgSetDouble(s, thickness[q]); + + snprintf(s, 128, cl, i); + strncat(s, "/span-m", 128); + fgSetDouble(s, 40000.0); + } + + for (; i < FGEnvironmentMgr::MAX_CLOUD_LAYERS; i++) { + snprintf(s, 128, cl, i); + strncat(s, "/coverage", 128); + fgSetString(s, "clear"); + + snprintf(s, 128, cl, i); + strncat(s, "/elevation-ft", 128); + fgSetDouble(s, -9999); + + snprintf(s, 128, cl, i); + strncat(s, "/thickness-ft", 128); + fgSetDouble(s, 0); + + snprintf(s, 128, cl, i); + strncat(s, "/span-m", 128); + fgSetDouble(s, 40000.0); + } + + fgSetDouble("/environment/metar/rain-norm", m->getRain()); + fgSetDouble("/environment/metar/hail-norm", m->getHail()); + fgSetDouble("/environment/metar/snow-norm", m->getSnow()); + fgSetBool("/environment/metar/snow-cover", m->getSnowCover()); +} + +void +FGClouds::update_env_config () +{ + fgSetupWind( fgGetDouble("/environment/metar/base-wind-range-from"), + fgGetDouble("/environment/metar/base-wind-range-to"), + fgGetDouble("/environment/metar/base-wind-speed-kt"), + fgGetDouble("/environment/metar/gust-wind-speed-kt") ); + + fgDefaultWeatherValue( "visibility-m", + fgGetDouble("/environment/metar/min-visibility-m") ); +#if 0 + set_temp_at_altitude( fgGetDouble("/environment/metar/temperature-degc"), + station_elevation_ft ); + set_dewpoint_at_altitude( fgGetDouble("/environment/metar/dewpoint-degc"), + station_elevation_ft ); +#endif + fgDefaultWeatherValue( "pressure-sea-level-inhg", + fgGetDouble("/environment/metar/pressure-inhg") ); +} + + +void FGClouds::setLayer( int iLayer, float alt_ft, string coverage, string layer_type ) { + double coverage_norm = 0.0; + if( coverage == "few" ) + coverage_norm = 2.0/8.0; // <1-2 + else if( coverage == "scattered" ) + coverage_norm = 4.0/8.0; // 3-4 + else if( coverage == "broken" ) + coverage_norm = 6.0/8.0; // 5-7 + else if( coverage == "overcast" ) + coverage_norm = 8.0/8.0; // 8 + + SGCloudField *layer3D = thesky->get_cloud_layer(iLayer)->get_layer3D(); + buildLayer(layer3D, layer_type, station_elevation_ft + alt_ft * SG_FEET_TO_METER, coverage_norm); +} + +void FGClouds::buildScenario( string scenario ) { + string fakeMetar=""; + string station = fgGetString("/environment/metar/station-id", "XXXX"); + + // fetch station elevation if exists + FGAirport a = globals->get_airports()->search( station ); + station_elevation_ft = a.getElevation(); + + for(int iLayer = 0 ; iLayer < thesky->get_cloud_layer_count(); iLayer++) { + thesky->get_cloud_layer(iLayer)->get_layer3D()->clear(); + } + + station += " 011000Z "; + if( scenario == "Fair weather" ) { + fakeMetar = "15003KT 12SM SCT033 FEW200 20/08 Q1015 NOSIG"; + setLayer(0, 3300.0, "scattered", "cu"); + } else if( scenario == "Thunderstorm" ) { + fakeMetar = "15012KT 08SM TSRA SCT040 BKN070 20/12 Q0995"; + setLayer(0, 4000.0, "scattered", "cb"); + setLayer(1, 7000.0, "scattered", "ns"); + } else + return; + FGMetar *m = new FGMetar( station + fakeMetar ); + update_metar_properties( m ); + update_env_config(); + fgSetString("/environment/metar/last-metar", m->getData()); + // TODO:desactivate real metar updates + if( scenario == "Fair weather" ) { + fgSetString("/environment/clouds/layer[1]/coverage", "cirrus"); + } +} + + +void FGClouds::build(void) { + string scenario = fgGetString("/environment/weather-scenario", "METAR"); + + if( scenario == "METAR" ) { + string realMetar = fgGetString("/environment/metar/last-metar", ""); + if( realMetar != "" ) { + FGMetar *m = new FGMetar( realMetar ); + update_metar_properties( m ); + update_env_config(); + } + buildMETAR(); + } + else + buildScenario( scenario ); + + // ... + if( snd_lightning == NULL ) + init(); +} diff --git a/src/Environment/fgclouds.hxx b/src/Environment/fgclouds.hxx new file mode 100644 index 000000000..dee821c1d --- /dev/null +++ b/src/Environment/fgclouds.hxx @@ -0,0 +1,75 @@ +// Build a cloud layer based on metar +// +// 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 _FGCLOUDS_HXX +#define _FGCLOUDS_HXX + +#ifdef HAVE_CONFIG +# include +#endif + +#include +#include +#include
+ +#include STL_STRING + +SG_USING_STD(string); + +class SGNewCloud; +class SGCloudField; +class FGMetar; + +class FGClouds { + +private: + SGNewCloud *buildCloud(SGPropertyNode *cloud_def_root, string name); + void buildLayer(SGCloudField *layer, string name, double alt, double coverage); + + void buildMETAR(void); + + void buildScenario( string scenario ); + + void setLayer( int iLayer, float alt_m, string coverage, string layer_type ); + + void update_metar_properties( FGMetar *m ); + + void FGClouds::update_env_config (); + + int update_event; + SGSoundSample *snd_lightning; + + float station_elevation_ft; + +public: + FGClouds(); + ~FGClouds(); + + void build(void); + + void init(void); + + int get_update_event(void) const; + void set_update_event(int count); + +}; + +#endif // _FGCLOUDS_HXX \ No newline at end of file diff --git a/src/Main/renderer.cxx b/src/Main/renderer.cxx index 7316d4206..afc92a537 100644 --- a/src/Main/renderer.cxx +++ b/src/Main/renderer.cxx @@ -53,6 +53,7 @@ # include # include #endif +#include #include #include