diff --git a/CMakeLists.txt b/CMakeLists.txt index 922d653f..f6729155 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required (VERSION 2.6.4) include (CheckFunctionExists) include (CheckIncludeFile) include (CheckCXXSourceCompiles) -include (CPack) + project(SimGear) @@ -18,6 +18,7 @@ SET(CPACK_PACKAGE_VENDOR "The FlightGear project") SET(CPACK_GENERATOR "TBZ2") SET(CPACK_INSTALL_CMAKE_PROJECTS ${CMAKE_CURRENT_BINARY_DIR};SimGear;ALL;/) + # split version string into components, note CMAKE_MATCH_0 is the entire regexp match string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)" CPACK_PACKAGE_VERSION ${SIMGEAR_VERSION} ) set(CPACK_PACKAGE_VERSION_MAJOR ${CMAKE_MATCH_1}) @@ -26,6 +27,15 @@ set(CPACK_PACKAGE_VERSION_PATCH ${CMAKE_MATCH_3}) message(STATUS "version is ${CPACK_PACKAGE_VERSION_MAJOR} dot ${CPACK_PACKAGE_VERSION_MINOR} dot ${CPACK_PACKAGE_VERSION_PATCH}") +set(CPACK_SOURCE_GENERATOR TBZ2) +set(CPACK_SOURCE_PACKAGE_FILE_NAME "simgear-${SIMGEAR_VERSION}" CACHE INTERNAL "tarball basename") +set(CPACK_SOURCE_IGNORE_FILES + "^${PROJECT_SOURCE_DIR}/.git;\\\\.gitignore;Makefile.am;~$;${CPACK_SOURCE_IGNORE_FILES}") + +message(STATUS "ignoring: ${CPACK_SOURCE_IGNORE_FILES}") + +include (CPack) + # We have some custom .cmake scripts not in the official distribution. set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/CMakeModules;${CMAKE_MODULE_PATH}") diff --git a/simgear/environment/CMakeLists.txt b/simgear/environment/CMakeLists.txt index 5a4dce1e..c59f98a7 100644 --- a/simgear/environment/CMakeLists.txt +++ b/simgear/environment/CMakeLists.txt @@ -5,3 +5,12 @@ set(HEADERS metar.hxx precipitation.hxx) set(SOURCES metar.cxx precipitation.cxx) simgear_component(environment environment "${SOURCES}" "${HEADERS}") + +add_executable(test_metar test_metar.cxx) +target_link_libraries(test_metar + sgenvironment sgstructure sgmisc sgdebug + ${CMAKE_THREAD_LIBS_INIT} + ${ZLIB_LIBRARY} + ${RT_LIBRARY}) + +add_test(metar ${EXECUTABLE_OUTPUT_PATH}/test_metar) \ No newline at end of file diff --git a/simgear/environment/metar.cxx b/simgear/environment/metar.cxx index c15c4a72..75109b75 100644 --- a/simgear/environment/metar.cxx +++ b/simgear/environment/metar.cxx @@ -32,7 +32,6 @@ #include #include -#include #include #include @@ -40,32 +39,28 @@ #define NaN SGMetarNaN +using std::string; +using std::map; +using std::vector; + /** - * The constructor takes a Metar string, or a four-letter ICAO code. In the - * latter case the metar string is downloaded from - * http://weather.noaa.gov/pub/data/observations/metar/stations/. + * The constructor takes a Metar string * The constructor throws sg_io_exceptions on failure. The "METAR" * keyword has no effect (apart from incrementing the group counter * @a grpcount) and can be left away. A keyword "SPECI" is * likewise accepted. * * @param m ICAO station id or metar string - * @param proxy proxy host (optional; default: "") - * @param port proxy port (optional; default: "80") - * @param auth proxy authorization information (optional; default: "") * * @par Examples: * @code * SGMetar *m = new SGMetar("METAR KSFO 061656Z 19004KT 9SM SCT100 OVC200 08/03 A3013"); * double t = m->getTemperature_F(); * delete m; - * - * SGMetar n("KSFO", "proxy.provider.foo", "3128", "proxy-password"); - * double d = n.getDewpoint_C(); + * @endcode */ -SGMetar::SGMetar(const string& m, const string& proxy, const string& port, - const string& auth, const time_t time) : +SGMetar::SGMetar(const string& m) : _grpcount(0), _x_proxy(false), _year(-1), @@ -87,16 +82,10 @@ SGMetar::SGMetar(const string& m, const string& proxy, const string& port, _snow(false), _cavok(false) { - if (m.length() == 4 && isalnum(m[0]) && isalnum(m[1]) && isalnum(m[2]) && isalnum(m[3])) { - for (int i = 0; i < 4; i++) - _icao[i] = toupper(m[i]); - _icao[4] = '\0'; - _data = loadData(_icao, proxy, port, auth, time); - } else { - _data = new char[m.length() + 2]; // make room for " \0" - strcpy(_data, m.c_str()); - _url = _data; - } + _data = new char[m.length() + 2]; // make room for " \0" + strcpy(_data, m.c_str()); + _url = _data; + normalizeData(); _m = _data; @@ -169,85 +158,6 @@ void SGMetar::useCurrentDate() _month = now.tm_mon + 1; } - -/** - * If called with "KSFO" loads data from - * @code - * http://weather.noaa.gov/pub/data/observations/metar/stations/KSFO.TXT. - * @endcode - * Throws sg_io_exception on failure. Gives up after waiting longer than 10 seconds. - * - * @param id four-letter ICAO Metar station code, e.g. "KSFO". - * @param proxy proxy host (optional; default: "") - * @param port proxy port (optional; default: "80") - * @param auth proxy authorization information (optional; default: "") - * @return pointer to Metar data string, allocated by new char[]. - * @see rfc2068.txt for proxy spec ("Proxy-Authorization") - */ -char *SGMetar::loadData(const char *id, const string& proxy, const string& port, - const string& auth, time_t time) -{ - const int buflen = 512; - char buf[2 * buflen]; - - string metar_server = "weather.noaa.gov"; - string host = proxy.empty() ? metar_server : proxy; - string path = "/pub/data/observations/metar/stations/"; - - path += string(id) + ".TXT"; - _url = "http://" + metar_server + path; - - SGSocket *sock = new SGSocket(host, port.empty() ? "80" : port, "tcp"); - sock->set_timeout(10000); - if (!sock->open(SG_IO_OUT)) { - delete sock; - throw sg_io_exception("cannot connect to ", sg_location(host)); - } - - string get = "GET "; - if (!proxy.empty()) - get += "http://" + metar_server; - - sprintf(buf, "%ld", time); - get += path + " HTTP/1.0\015\012X-Time: " + buf + "\015\012"; - get += "Host: " + metar_server + "\015\012"; - - if (!auth.empty()) - get += "Proxy-Authorization: " + auth + "\015\012"; - - get += "\015\012"; - sock->writestring(get.c_str()); - - int i; - - // skip HTTP header - while ((i = sock->readline(buf, buflen))) { - if (i <= 2 && isspace(buf[0]) && (!buf[1] || isspace(buf[1]))) - break; - if (!strncmp(buf, "X-MetarProxy: ", 13)) - _x_proxy = true; - } - if (i) { - i = sock->readline(buf, buflen); - if (i) - sock->readline(&buf[i], buflen); - } - - sock->close(); - delete sock; - - char *b = buf; - scanBoundary(&b); - if (*b == '<') - throw sg_io_exception("no metar data available from ", - sg_location(_url)); - - char *metar = new char[strlen(b) + 2]; // make room for " \0" - strcpy(metar, b); - return metar; -} - - /** * Replace any number of subsequent spaces by just one space, and add * a trailing space. This makes scanning for things like "ALL RWY" easier. diff --git a/simgear/environment/metar.hxx b/simgear/environment/metar.hxx index e501a0c4..26e8523e 100644 --- a/simgear/environment/metar.hxx +++ b/simgear/environment/metar.hxx @@ -29,18 +29,12 @@ #include -using std::vector; -using std::map; -using std::string; - -const double SGMetarNaN = -1E20; -#define NaN SGMetarNaN - struct Token { const char *id; const char *text; }; +const double SGMetarNaN = -1E20; class SGMetar; @@ -48,7 +42,7 @@ class SGMetarVisibility { friend class SGMetar; public: SGMetarVisibility() : - _distance(NaN), + _distance(SGMetarNaN), _direction(-1), _modifier(EQUALS), _tendency(NONE) {} @@ -70,8 +64,8 @@ public: void set(double dist, int dir = -1, int mod = -1, int tend = -1); inline double getVisibility_m() const { return _distance; } - inline double getVisibility_ft() const { return _distance == NaN ? NaN : _distance * SG_METER_TO_FEET; } - inline double getVisibility_sm() const { return _distance == NaN ? NaN : _distance * SG_METER_TO_SM; } + inline double getVisibility_ft() const { return _distance == SGMetarNaN ? SGMetarNaN : _distance * SG_METER_TO_FEET; } + inline double getVisibility_sm() const { return _distance == SGMetarNaN ? SGMetarNaN : _distance * SG_METER_TO_SM; } inline int getDirection() const { return _direction; } inline int getModifier() const { return _modifier; } inline int getTendency() const { return _tendency; } @@ -93,8 +87,8 @@ public: _deposit_string(0), _extent(-1), _extent_string(0), - _depth(NaN), - _friction(NaN), + _depth(SGMetarNaN), + _friction(SGMetarNaN), _friction_string(0), _comment(0), _wind_shear(false) {} @@ -146,14 +140,14 @@ public: static const char * COVERAGE_BROKEN_STRING; static const char * COVERAGE_OVERCAST_STRING; - SGMetarCloud() : _coverage(COVERAGE_NIL), _altitude(NaN), _type(0), _type_long(0) {} + SGMetarCloud() : _coverage(COVERAGE_NIL), _altitude(SGMetarNaN), _type(0), _type_long(0) {} void set(double alt, Coverage cov = COVERAGE_NIL ); inline Coverage getCoverage() const { return _coverage; } static Coverage getCoverage( const std::string & coverage ); inline double getAltitude_m() const { return _altitude; } - inline double getAltitude_ft() const { return _altitude == NaN ? NaN : _altitude * SG_METER_TO_FEET; } + inline double getAltitude_ft() const { return _altitude == SGMetarNaN ? SGMetarNaN : _altitude * SG_METER_TO_FEET; } inline const char *getTypeString() const { return _type; } inline const char *getTypeLongString() const { return _type_long; } @@ -167,8 +161,7 @@ protected: class SGMetar { public: - SGMetar(const string& m, const string& proxy = "", const string& port = "", - const string &auth = "", const time_t time = 0); + SGMetar(const std::string& m); ~SGMetar(); enum ReportType { @@ -189,8 +182,8 @@ public: Weather() { intensity = NIL; vincinity = false; } Intensity intensity; bool vincinity; - vector descriptions; - vector phenomena; + std::vector descriptions; + std::vector phenomena; }; inline const char *getData() const { return _data; } @@ -206,14 +199,14 @@ public: inline int getWindDir() const { return _wind_dir; } inline double getWindSpeed_mps() const { return _wind_speed; } - inline double getWindSpeed_kmh() const { return _wind_speed == NaN ? NaN : _wind_speed * SG_MPS_TO_KMH; } - inline double getWindSpeed_kt() const { return _wind_speed == NaN ? NaN : _wind_speed * SG_MPS_TO_KT; } - inline double getWindSpeed_mph() const { return _wind_speed == NaN ? NaN : _wind_speed * SG_MPS_TO_MPH; } + inline double getWindSpeed_kmh() const { return _wind_speed == SGMetarNaN ? SGMetarNaN : _wind_speed * SG_MPS_TO_KMH; } + inline double getWindSpeed_kt() const { return _wind_speed == SGMetarNaN ? SGMetarNaN : _wind_speed * SG_MPS_TO_KT; } + inline double getWindSpeed_mph() const { return _wind_speed == SGMetarNaN ? SGMetarNaN : _wind_speed * SG_MPS_TO_MPH; } inline double getGustSpeed_mps() const { return _gust_speed; } - inline double getGustSpeed_kmh() const { return _gust_speed == NaN ? NaN : _gust_speed * SG_MPS_TO_KMH; } - inline double getGustSpeed_kt() const { return _gust_speed == NaN ? NaN : _gust_speed * SG_MPS_TO_KT; } - inline double getGustSpeed_mph() const { return _gust_speed == NaN ? NaN : _gust_speed * SG_MPS_TO_MPH; } + inline double getGustSpeed_kmh() const { return _gust_speed == SGMetarNaN ? SGMetarNaN : _gust_speed * SG_MPS_TO_KMH; } + inline double getGustSpeed_kt() const { return _gust_speed == SGMetarNaN ? SGMetarNaN : _gust_speed * SG_MPS_TO_KT; } + inline double getGustSpeed_mph() const { return _gust_speed == SGMetarNaN ? SGMetarNaN : _gust_speed * SG_MPS_TO_MPH; } inline int getWindRangeFrom() const { return _wind_range_from; } inline int getWindRangeTo() const { return _wind_range_to; } @@ -224,11 +217,11 @@ public: inline const SGMetarVisibility *getDirVisibility() const { return _dir_visibility; } inline double getTemperature_C() const { return _temp; } - inline double getTemperature_F() const { return _temp == NaN ? NaN : 1.8 * _temp + 32; } + inline double getTemperature_F() const { return _temp == SGMetarNaN ? SGMetarNaN : 1.8 * _temp + 32; } inline double getDewpoint_C() const { return _dewp; } - inline double getDewpoint_F() const { return _dewp == NaN ? NaN : 1.8 * _dewp + 32; } - inline double getPressure_hPa() const { return _pressure == NaN ? NaN : _pressure / 100; } - inline double getPressure_inHg() const { return _pressure == NaN ? NaN : _pressure * SG_PA_TO_INHG; } + inline double getDewpoint_F() const { return _dewp == SGMetarNaN ? SGMetarNaN : 1.8 * _dewp + 32; } + inline double getPressure_hPa() const { return _pressure == SGMetarNaN ? SGMetarNaN : _pressure / 100; } + inline double getPressure_inHg() const { return _pressure == SGMetarNaN ? SGMetarNaN : _pressure * SG_PA_TO_INHG; } inline int getRain() const { return _rain; } inline int getHail() const { return _hail; } @@ -237,13 +230,13 @@ public: double getRelHumidity() const; - inline const vector& getClouds() const { return _clouds; } - inline const map& getRunways() const { return _runways; } - inline const vector& getWeather() const { return _weather; } - inline const vector getWeather2() const { return _weather2; } + inline const std::vector& getClouds() const { return _clouds; } + inline const std::map& getRunways() const { return _runways; } + inline const std::vector& getWeather() const { return _weather; } + inline const std::vector getWeather2() const { return _weather2; } protected: - string _url; + std::string _url; int _grpcount; bool _x_proxy; char *_data; @@ -267,15 +260,15 @@ protected: int _hail; int _snow; bool _cavok; - vector _weather2; + std::vector _weather2; SGMetarVisibility _min_visibility; SGMetarVisibility _max_visibility; SGMetarVisibility _vert_visibility; SGMetarVisibility _dir_visibility[8]; - vector _clouds; - map _runways; - vector _weather; + std::vector _clouds; + std::map _runways; + std::vector _weather; bool scanPreambleDate(); bool scanPreambleTime(); @@ -303,10 +296,7 @@ protected: int scanNumber(char **str, int *num, int min, int max = 0); bool scanBoundary(char **str); const struct Token *scanToken(char **str, const struct Token *list); - char *loadData(const char *id, const string& proxy, const string& port, - const string &auth, time_t time); void normalizeData(); }; -#undef NaN #endif // _METAR_HXX diff --git a/simgear/environment/test_metar.cxx b/simgear/environment/test_metar.cxx new file mode 100644 index 00000000..814094b2 --- /dev/null +++ b/simgear/environment/test_metar.cxx @@ -0,0 +1,76 @@ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include +#include +#include + +#ifdef _MSC_VER +# define random rand +#endif + +#include +#include + +#include "metar.hxx" + +using std::cout; +using std::cerr; +using std::endl; +using std::string; + +#define COMPARE(a, b) \ + if ((a) != (b)) { \ + cerr << "failed:" << #a << " != " << #b << endl; \ + cerr << "\tgot:" << a << endl; \ + exit(1); \ + } + +#define FUZZY_COMPARE(a, b, epsilon) \ + if (fabs(a - b) > epsilon) { \ + cerr << "failed:" << #a << " != " << #b << endl; \ + cerr << "\tgot:" << a << endl; \ + cerr << "\tepsilon:" << epsilon << endl; \ + } + +#define VERIFY(a) \ + if (!(a)) { \ + cerr << "failed:" << #a << endl; \ + exit(1); \ + } + +const double TEST_EPSILON = 1e-9; + +void test_basic() +{ + SGMetar m1("2011/10/20 11:25 EHAM 201125Z 27012KT 240V300 9999 VCSH FEW025CB SCT048 10/05 Q1025 TEMPO VRB03KT"); + COMPARE(m1.getYear(), 2011); + COMPARE(m1.getMonth(), 10); + COMPARE(m1.getDay(), 20); + COMPARE(m1.getHour(), 11); + COMPARE(m1.getMinute(), 25); + COMPARE(m1.getReportType(), -1); // should default to NIL? + + COMPARE(m1.getWindDir(), 270); + FUZZY_COMPARE(m1.getWindSpeed_kt(), 12, TEST_EPSILON); + + FUZZY_COMPARE(m1.getTemperature_C(), 10, TEST_EPSILON); + FUZZY_COMPARE(m1.getDewpoint_C(), 5, TEST_EPSILON); + FUZZY_COMPARE(m1.getPressure_hPa(), 1025, TEST_EPSILON); +} + +int main(int argc, char* argv[]) +{ + try { + test_basic(); + } catch (sg_exception& e) { + cerr << "got exception:" << e.getMessage() << endl; + return -1; + } + + return 0; +} diff --git a/simgear/misc/sg_dir.cxx b/simgear/misc/sg_dir.cxx index 95f9949d..8922b030 100644 --- a/simgear/misc/sg_dir.cxx +++ b/simgear/misc/sg_dir.cxx @@ -47,22 +47,37 @@ using std::string; namespace simgear { -Dir::Dir() +Dir::Dir() : + _removeOnDestroy(false) { } Dir::Dir(const SGPath& path) : - _path(path) + _path(path), + _removeOnDestroy(false) { _path.set_cached(false); // disable caching, so create/remove work } Dir::Dir(const Dir& rel, const SGPath& relPath) : - _path(rel.file(relPath.str())) + _path(rel.file(relPath.str())), + _removeOnDestroy(false) { _path.set_cached(false); // disable caching, so create/remove work } +Dir::~Dir() +{ + if (_removeOnDestroy) { + remove(true); + } +} + +void Dir::setRemoveOnDestroy() +{ + _removeOnDestroy = true; +} + Dir Dir::current() { #ifdef _WIN32 @@ -79,7 +94,7 @@ Dir Dir::tempDir(const std::string& templ) { #ifdef HAVE_MKDTEMP char buf[1024]; - char* tempPath = ::getenv("TMPDIR"); + const char* tempPath = ::getenv("TMPDIR"); if (!tempPath) { tempPath = "/tmp/"; } diff --git a/simgear/misc/sg_dir.hxx b/simgear/misc/sg_dir.hxx index 7befbc8c..109a0d4b 100644 --- a/simgear/misc/sg_dir.hxx +++ b/simgear/misc/sg_dir.hxx @@ -43,7 +43,15 @@ namespace simgear { public: Dir(); - + ~Dir(); + + /** + * when this directory object is destroyed, remove the corresponding + * diretory (and its contents) from the disk. Often used with temporary + * directories to ensure they are cleaned up. + */ + void setRemoveOnDestroy(); + static Dir current(); /** @@ -93,6 +101,7 @@ namespace simgear Dir parent() const; private: mutable SGPath _path; + bool _removeOnDestroy; }; } // of namespace simgear diff --git a/simgear/misc/sg_path.cxx b/simgear/misc/sg_path.cxx index 53504a20..bff1a61c 100644 --- a/simgear/misc/sg_path.cxx +++ b/simgear/misc/sg_path.cxx @@ -471,3 +471,16 @@ bool SGPath::operator!=(const SGPath& other) const return (path != other.path); } +bool SGPath::rename(const SGPath& newName) +{ + if (::rename(c_str(), newName.c_str()) != 0) { + SG_LOG(SG_IO, SG_WARN, "renamed failed: from " << str() << " to " << newName.str() + << " reason: " << strerror(errno)); + return false; + } + + path = newName.path; + _cached = false; + return true; +} + diff --git a/simgear/misc/sg_path.hxx b/simgear/misc/sg_path.hxx index 27b375d9..e71c5f54 100644 --- a/simgear/misc/sg_path.hxx +++ b/simgear/misc/sg_path.hxx @@ -213,6 +213,13 @@ public: * modification time of the file */ time_t modTime() const; + + /** + * rename the file / directory we point at, to a new name + * this may fail if the new location is on a different volume / share, + * or if the destination already exists, or is not writeable + */ + bool rename(const SGPath& newName); private: void fix(); diff --git a/simgear/misc/strutils.cxx b/simgear/misc/strutils.cxx index 5a669a6b..b1f74318 100644 --- a/simgear/misc/strutils.cxx +++ b/simgear/misc/strutils.cxx @@ -278,6 +278,19 @@ namespace simgear { return v1parts.size() - v2parts.size(); } + string join(const string_list& l, const string& joinWith) + { + string result; + unsigned int count = l.size(); + for (unsigned int i=0; i < count; ++i) { + result += l[i]; + if (i < (count - 1)) { + result += joinWith; + } + } + + return result; + } } // end namespace strutils diff --git a/simgear/misc/strutils.hxx b/simgear/misc/strutils.hxx index 091ba823..9bd1dbe1 100644 --- a/simgear/misc/strutils.hxx +++ b/simgear/misc/strutils.hxx @@ -33,6 +33,7 @@ #include #include +typedef std::vector < std::string > string_list; namespace simgear { namespace strutils { @@ -93,11 +94,17 @@ namespace simgear { * resulting in at most maxsplit+1 words. * @return Array of words. */ - std::vector + string_list split( const std::string& s, const char* sep = 0, int maxsplit = 0 ); + /** + * create a single string by joining the elements of a list with + * another string. + */ + std::string join(const string_list& l, const std::string& joinWith = ""); + /** * Test if a string starts with a string * diff --git a/simgear/misc/strutils_test.cxx b/simgear/misc/strutils_test.cxx index 8369f4ea..6387b47c 100644 --- a/simgear/misc/strutils_test.cxx +++ b/simgear/misc/strutils_test.cxx @@ -63,6 +63,20 @@ int main (int ac, char ** av) // since we compare numerically, leasing zeros shouldn't matter VERIFY(compare_versions("0.06.7", "0.6.07") == 0); + string_list la = split("zero one two three four five"); + COMPARE(la[2], "two"); + COMPARE(la[5], "five"); + COMPARE(la.size(), 6); + + string_list lb = split("alpha:beta:gamma:delta", ":", 2); + COMPARE(lb.size(), 3); + COMPARE(lb[0], "alpha"); + COMPARE(lb[1], "beta"); + COMPARE(lb[2], "gamma:delta"); + + std::string j = join(la, "&"); + COMPARE(j, "zero&one&two&three&four&five"); + cout << "all tests passed successfully!" << endl; return 0; } diff --git a/simgear/scene/sky/cloudfield.cxx b/simgear/scene/sky/cloudfield.cxx index 2b145b57..086899d7 100644 --- a/simgear/scene/sky/cloudfield.cxx +++ b/simgear/scene/sky/cloudfield.cxx @@ -68,8 +68,8 @@ float SGCloudField::fieldSize = 50000.0f; double SGCloudField::timer_dt = 0.0; float SGCloudField::view_distance = 20000.0f; bool SGCloudField::wrap = true; -float SGCloudField::RADIUS_LEVEL_1 = 20000.0f; -float SGCloudField::RADIUS_LEVEL_2 = 5000.0f; +float SGCloudField::RADIUS_LEVEL_1 = 5000.0f; +float SGCloudField::RADIUS_LEVEL_2 = 1000.0f; SGVec3f SGCloudField::view_vec, SGCloudField::view_X, SGCloudField::view_Y; @@ -81,7 +81,9 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, if (placed_root->getNumChildren() == 0) return false; SGVec3 cart; - SGGeodesy::SGGeodToCart(SGGeod::fromRadFt(lon, lat, 0.0f), cart); + SGGeod new_pos = SGGeod::fromRadFt(lon, lat, 0.0f); + + SGGeodesy::SGGeodToCart(new_pos, cart); osg::Vec3f osg_pos = toOsg(cart); osg::Quat orient = toOsg(SGQuatd::fromLonLatRad(lon, lat) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0))); @@ -93,8 +95,6 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, osg::Vec3f wind = osg::Vec3f(-cos((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt, sin((direction + 180)* SGD_DEGREES_TO_RADIANS) * speed * dt, 0.0f); - //cout << "Wind: " << direction << "@" << speed << "\n"; - osg::Vec3f windosg = field_transform->getAttitude() * wind; field_transform->setPosition(field_transform->getPosition() + windosg); @@ -108,39 +108,40 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, field_transform->setPosition(osg_pos); field_transform->setAttitude(orient); old_pos = osg_pos; - } else { + } else { + osg::Quat fta = field_transform->getAttitude(); + osg::Quat ftainv = field_transform->getAttitude().inverse(); + // delta is the vector from the old position to the new position in cloud-coords - osg::Vec3f delta = field_transform->getAttitude().inverse() * (osg_pos - old_pos); - //cout << "Delta: " << delta.length() << "\n"; - - for (unsigned int i = 0; i < placed_root->getNumChildren(); i++) { - osg::ref_ptr lodnode1 = (osg::LOD*) placed_root->getChild(i); - osg::Vec3f v = delta - lodnode1->getCenter(); - - if ((v.x() < -0.5*fieldSize) || - (v.x() > 0.5*fieldSize) || - (v.y() < -0.5*fieldSize) || - (v.y() > 0.5*fieldSize) ) { - //cout << "V: " << v.x() << ", " << v.y() << "\n"; - - osg::Vec3f shift = osg::Vec3f(0.0f, 0.0f, 0.0f); - if (v.x() > 0.5*fieldSize) { shift += osg::Vec3f(fieldSize, 0.0f, 0.0f); } - if (v.x() < -0.5*fieldSize) { shift -= osg::Vec3f(fieldSize, 0.0f, 0.0f); } - - if (v.y() > 0.5*fieldSize) { shift += osg::Vec3f(0.0f, fieldSize, 0.0f); } - if (v.y() < -0.5*fieldSize) { shift -= osg::Vec3f(0.0f, fieldSize, 0.0f); } - - //cout << "Shift: " << shift.x() << ", " << shift.y() << "\n\n"; - - for (unsigned int j = 0; j < lodnode1->getNumChildren(); j++) { - osg::ref_ptr lodnode2 = (osg::LOD*) lodnode1->getChild(j); - for (unsigned int k = 0; k < lodnode2->getNumChildren(); k++) { - osg::ref_ptr pat =(osg::PositionAttitudeTransform*) lodnode2->getChild(k); - pat->setPosition(pat->getPosition() + shift); - } - } + osg::Vec3f delta = ftainv * (osg_pos - old_pos); + old_pos = osg_pos; + + // Check if any of the clouds should be moved. + for(CloudHash::const_iterator itr = cloud_hash.begin(), end = cloud_hash.end(); + itr != end; + ++itr) { + + osg::ref_ptr pat = itr->second; + osg::Vec3f currpos = field_transform->getPosition() + fta * pat->getPosition(); + + // Determine the vector from the new position to the cloud in cloud-space. + osg::Vec3f w = ftainv * (currpos - toOsg(cart)); + + // Determine a course if required. Note that this involves some axis translation. + float x = 0.0; + float y = 0.0; + if (w.x() > 0.6*fieldSize) { y = fieldSize; } + if (w.x() < -0.6*fieldSize) { y = -fieldSize; } + if (w.y() > 0.6*fieldSize) { x = -fieldSize; } + if (w.y() < -0.6*fieldSize) { x = fieldSize; } + + if ((x != 0.0) || (y != 0.0)) { + removeCloudFromTree(pat); + SGGeod p = SGGeod::fromCart(toSG(field_transform->getPosition() + + fta * pat->getPosition())); + addCloudToTree(pat, p, x, y); } - } + } } // Render the clouds in order from farthest away layer to nearest one. @@ -231,28 +232,32 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t float lon, float lat, float alt, float x, float y) { // Get the base position - SGGeod loc = SGGeod::fromDegFt(lon, lat, 0.0); - + SGGeod loc = SGGeod::fromDegFt(lon, lat, alt); + addCloudToTree(transform, loc, x, y); +} + + +void SGCloudField::addCloudToTree(osg::ref_ptr transform, + SGGeod loc, float x, float y) { + + float alt = loc.getElevationFt(); // Determine any shift by x/y if ((x != 0.0f) || (y != 0.0f)) { - double crs; - - if (y == 0.0f) { - crs = (x < 0.0f) ? -90.0 : 90.0; - } else { - crs = SG_RADIANS_TO_DEGREES * tan(x/y); - } - + double crs = 90.0 - SG_RADIANS_TO_DEGREES * atan2(y, x); double dst = sqrt(x*x + y*y); double endcrs; - SGGeod base_pos = SGGeod::fromDegFt(lon, lat, 0.0f); + SGGeod base_pos = SGGeod::fromGeodFt(loc, 0.0f); SGGeodesy::direct(base_pos, crs, dst, loc, endcrs); } - // The direct call provides the position at 0 alt, so adjust as required. - loc.setElevationFt(alt); + // The direct call provides the position at 0 alt, so adjust as required. + loc.setElevationFt(alt); + addCloudToTree(transform, loc); +} + +void SGCloudField::addCloudToTree(osg::ref_ptr transform, SGGeod loc) { // Work out where this cloud should go in OSG coordinates. SGVec3 cart; SGGeodesy::SGGeodToCart(loc, cart); @@ -266,16 +271,17 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t // Convert to the scenegraph orientation where we just rotate around // the y axis 180 degrees. osg::Quat orient = toOsg(SGQuatd::fromLonLatDeg(loc.getLongitudeDeg(), loc.getLatitudeDeg()) * SGQuatd::fromRealImag(0, SGVec3d(0, 1, 0))); - - field_transform->setPosition(toOsg(fieldcenter)); + + osg::Vec3f osg_pos = toOsg(fieldcenter); + + field_transform->setPosition(osg_pos); field_transform->setAttitude(orient); - old_pos = toOsg(fieldcenter); + old_pos = osg_pos; } - + + // Convert position to cloud-coordinates pos = pos - field_transform->getPosition(); - - //pos = orient.inverse() * pos; - pos = field_transform->getAttitude().inverse() * pos; + pos = field_transform->getAttitude().inverse() * pos; // We have a two level dynamic quad tree which the cloud will be added // to. If there are no appropriate nodes in the quad tree, they are @@ -288,7 +294,6 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t lodnode1 = (osg::LOD*) placed_root->getChild(i); if ((lodnode1->getCenter() - pos).length2() < RADIUS_LEVEL_1*RADIUS_LEVEL_1) { // New cloud is within RADIUS_LEVEL_1 of the center of the LOD node. - //cout << "Adding cloud to existing LoD level 1 node. Distance:" << (lodnode1->getCenter() - pos).length() << "\n"; found = true; } } @@ -296,7 +301,6 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t if (!found) { lodnode1 = new osg::LOD(); placed_root->addChild(lodnode1.get()); - //cout << "Adding cloud to new LoD node\n"; } // Now check if there is a second level LOD node at an appropriate distance @@ -306,17 +310,15 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t lodnode = (osg::LOD*) lodnode1->getChild(j); if ((lodnode->getCenter() - pos).length2() < RADIUS_LEVEL_2*RADIUS_LEVEL_2) { // We've found the right leaf LOD node - //cout << "Found existing LOD leaf node. Distance:"<< (lodnode->getCenter() - pos).length() << "\n"; found = true; - } } + } if (!found) { // No suitable leave node was found, so we need to add one. lodnode = new osg::LOD(); lodnode1->addChild(lodnode, 0.0f, 4*RADIUS_LEVEL_1); - //cout << "Adding cloud to new LoD node\n"; -} + } transform->setPosition(pos); lodnode->addChild(transform.get(), 0.0f, view_distance); diff --git a/simgear/scene/sky/cloudfield.hxx b/simgear/scene/sky/cloudfield.hxx index 77f504b6..1de2fce3 100644 --- a/simgear/scene/sky/cloudfield.hxx +++ b/simgear/scene/sky/cloudfield.hxx @@ -70,7 +70,7 @@ private: bool visible; }; - float Rnd(float); + float Rnd(float); // Radius of the LoD nodes for the dynamic quadtrees. static float RADIUS_LEVEL_1; @@ -79,23 +79,25 @@ private: // this is a relative position only, with that we can move all clouds at once SGVec3f relative_position; - osg::ref_ptr field_root; + osg::ref_ptr field_root; osg::ref_ptr placed_root; osg::ref_ptr field_transform; - osg::ref_ptr altitude_transform; - osg::ref_ptr field_lod; + osg::ref_ptr altitude_transform; + osg::ref_ptr field_lod; osg::Vec3f old_pos; CloudHash cloud_hash; - struct CloudFog : public simgear::Singleton - { - CloudFog(); - osg::ref_ptr fog; - }; + struct CloudFog : public simgear::Singleton + { + CloudFog(); + osg::ref_ptr fog; + }; void removeCloudFromTree(osg::ref_ptr transform); void addCloudToTree(osg::ref_ptr transform, float lon, float lat, float alt, float x, float y); + void addCloudToTree(osg::ref_ptr transform, SGGeod loc, float x, float y); + void addCloudToTree(osg::ref_ptr transform, SGGeod loc); void applyVisRangeAndCoverage(void); public: @@ -122,44 +124,44 @@ public: /** - * 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 dt the time elapsed since the last call - * @param asl altitude of the layer - * @param speed of cloud layer movement (due to wind) - * @param direction of cloud layer movement (due to wind) - */ - bool reposition( const SGVec3f& p, const SGVec3f& up, - double lon, double lat, double dt, int asl, float speed, float direction); + * 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 dt the time elapsed since the last call + * @param asl altitude of the layer + * @param speed of cloud layer movement (due to wind) + * @param direction of cloud layer movement (due to wind) + */ + bool reposition( const SGVec3f& p, const SGVec3f& up, + double lon, double lat, double dt, int asl, float speed, float direction); - osg::Group* getNode() { return field_root.get(); } + osg::Group* getNode() { return field_root.get(); } - // visibility distance for clouds in meters - static float CloudVis; + // visibility distance for clouds in meters + static float CloudVis; - static SGVec3f view_vec, view_X, view_Y; + static SGVec3f view_vec, view_X, view_Y; - static float view_distance; - static double timer_dt; - static float fieldSize; - static bool wrap; - - static bool getWrap(void) { return wrap; } - static void setWrap(bool w) { wrap = w; } + static float view_distance; + static double timer_dt; + static float fieldSize; + static bool wrap; - static float getVisRange(void) { return view_distance; } - static void setVisRange(float d) { view_distance = d; } - void applyVisRange(void); - - static osg::Fog* getFog() - { - return CloudFog::instance()->fog.get(); - } - static void updateFog(double visibility, const osg::Vec4f& color); + static bool getWrap(void) { return wrap; } + static void setWrap(bool w) { wrap = w; } + + static float getVisRange(void) { return view_distance; } + static void setVisRange(float d) { view_distance = d; } + void applyVisRange(void); + + static osg::Fog* getFog() + { + return CloudFog::instance()->fog.get(); + } + static void updateFog(double visibility, const osg::Vec4f& color); }; #endif // _CLOUDFIELD_HXX diff --git a/simgear/scene/sky/newcloud.cxx b/simgear/scene/sky/newcloud.cxx index 0e9b7325..6cd0a93c 100644 --- a/simgear/scene/sky/newcloud.cxx +++ b/simgear/scene/sky/newcloud.cxx @@ -135,11 +135,12 @@ osg::Geometry* SGNewCloud::createOrthQuad(float w, float h, int varieties_x, int 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); + float ch = h*0.5f; + + v[0].set(0.0f, -cw, -ch); + v[1].set(0.0f, cw, -ch); + v[2].set(0.0f, cw, ch); + v[3].set(0.0f, -cw, ch); // The texture coordinate range is not the // entire coordinate space - as the texture