diff --git a/simgear/package/Catalog.cxx b/simgear/package/Catalog.cxx index de74d19d..5092b888 100644 --- a/simgear/package/Catalog.cxx +++ b/simgear/package/Catalog.cxx @@ -81,17 +81,27 @@ bool checkVersion(const std::string& aVersion, SGPropertyNode_ptr props) } return false; } + +SGPropertyNode_ptr alternateForVersion(const std::string& aVersion, SGPropertyNode_ptr props) +{ + for (auto v : props->getChildren("alternate-version")) { + for (auto versionSpec : v->getChildren("version")) { + if (checkVersionString(aVersion, versionSpec->getStringValue())) { + return v; + } + } + } + + return {}; +} std::string redirectUrlForVersion(const std::string& aVersion, SGPropertyNode_ptr props) { - for (SGPropertyNode* v : props->getChildren("alternate-version")) { - std::string s(v->getStringValue("version")); - if (checkVersionString(aVersion, s)) { - return v->getStringValue("url");; - } - } + auto node = alternateForVersion(aVersion, props); + if (node) + return node->getStringValue("url");; - return std::string(); + return {}; } ////////////////////////////////////////////////////////////////////////////// @@ -139,19 +149,15 @@ protected: std::string ver(m_owner->root()->applicationVersion()); if (!checkVersion(ver, props)) { - SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() << ", but version required " << ver); - // check for a version redirect entry - std::string url = redirectUrlForVersion(ver, props); - if (!url.empty()) { - SG_LOG(SG_GENERAL, SG_WARN, "redirecting from " << m_owner->url() << - " to \n\t" << url); - - // update the URL and kick off a new request - m_owner->setUrl(url); - Downloader* dl = new Downloader(m_owner, url); - m_owner->root()->makeHTTPRequest(dl); + auto alt = alternateForVersion(ver, props); + if (alt) { + SG_LOG(SG_GENERAL, SG_WARN, "have alternate version of package at:" + << alt->getStringValue("url")); + m_owner->processAlternate(alt); } else { + SG_LOG(SG_GENERAL, SG_WARN, "downloaded catalog " << m_owner->url() + << ", but app version " << ver << " is not comaptible"); m_owner->refreshComplete(Delegate::FAIL_VERSION); } @@ -228,7 +234,7 @@ CatalogRef Catalog::createFromPath(Root* aRoot, const SGPath& aPath) bool versionCheckOk = checkVersion(aRoot->applicationVersion(), props); if (!versionCheckOk) { - SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: need version: " + SG_LOG(SG_GENERAL, SG_INFO, "catalog at:" << aPath << " failed version check: app version: " << aRoot->applicationVersion()); // keep the catalog but mark it as needing an update } else { @@ -598,6 +604,50 @@ bool Catalog::isEnabled() const } } +void Catalog::processAlternate(SGPropertyNode_ptr alt) +{ + std::string altId; + const auto idPtr = alt->getStringValue("id"); + if (idPtr) { + altId = std::string(idPtr); + } + + std::string altUrl; + if (alt->getStringValue("url")) { + altUrl = std::string(alt->getStringValue("url")); + } + + CatalogRef existing; + if (!altId.empty()) { + existing = root()->getCatalogById(altId); + } else { + existing = root()->getCatalogByUrl(altUrl); + } + + if (existing && (existing != this)) { + // we already have the alternate, so just go quiet here + changeStatus(Delegate::FAIL_VERSION); + return; + } + + // we have an alternate ID, and it's differnt from our ID, so let's + // define a new catalog + if (!altId.empty()) { + SG_LOG(SG_GENERAL, SG_INFO, "Adding new catalog:" << altId << " as version alternate for " << id()); + // new catalog being added + createFromUrl(root(), altUrl); + + // and we can go idle now + changeStatus(Delegate::FAIL_VERSION); + return; + } + + SG_LOG(SG_GENERAL, SG_INFO, "Migrating catalog " << id() << " to new URL:" << altUrl); + setUrl(altUrl); + Downloader* dl = new Downloader(this, altUrl); + root()->makeHTTPRequest(dl); +} + } // of namespace pkg } // of namespace simgear diff --git a/simgear/package/Catalog.hxx b/simgear/package/Catalog.hxx index c0a6edd8..f300690a 100644 --- a/simgear/package/Catalog.hxx +++ b/simgear/package/Catalog.hxx @@ -179,6 +179,8 @@ private: void changeStatus(Delegate::StatusCode newStatus); + void processAlternate(SGPropertyNode_ptr alt); + Root* m_root; SGPropertyNode_ptr m_props; SGPath m_installRoot; diff --git a/simgear/package/CatalogTest.cxx b/simgear/package/CatalogTest.cxx index d0d1d93a..8b9f3435 100644 --- a/simgear/package/CatalogTest.cxx +++ b/simgear/package/CatalogTest.cxx @@ -741,6 +741,130 @@ void testVersionMigrate(HTTP::Client* cl) } } +void testVersionMigrateToId(HTTP::Client* cl) +{ + global_catalogVersion = 2; // version which has migration info + SGPath rootPath(simgear::Dir::current().path()); + rootPath.append("cat_migrate_version_id"); + simgear::Dir pd(rootPath); + pd.removeChildren(); + + { + pkg::RootRef root(new pkg::Root(rootPath, "8.1.2")); + root->setHTTPClient(cl); + + pkg::CatalogRef c = pkg::Catalog::createFromUrl(root.ptr(), "http://localhost:2000/catalogTest1/catalog.xml"); + waitForUpdateComplete(cl, root); + SG_VERIFY(c->isEnabled()); + + // install a package + pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG"); + SG_CHECK_EQUAL(p1->id(), "b737-NG"); + pkg::InstallRef ins = p1->install(); + SG_VERIFY(ins->isQueued()); + waitForUpdateComplete(cl, root); + SG_VERIFY(p1->isInstalled()); + } + + // change version to an alternate one + { + pkg::RootRef root(new pkg::Root(rootPath, "7.5")); + root->setHTTPClient(cl); + + // this should cause the alternate package to be loaded + root->refresh(true); + waitForUpdateComplete(cl, root); + + pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1"); + SG_VERIFY(!cat->isEnabled()); + SG_CHECK_EQUAL(cat->status(), pkg::Delegate::FAIL_VERSION); + SG_CHECK_EQUAL(cat->id(), "org.flightgear.test.catalog1"); + SG_CHECK_EQUAL(cat->url(), "http://localhost:2000/catalogTest1/catalog.xml"); + + auto enabledCats = root->catalogs(); + auto it = std::find(enabledCats.begin(), enabledCats.end(), cat); + SG_VERIFY(it == enabledCats.end()); + + // ensure existing package is still installed + pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG"); + SG_VERIFY(p1 != pkg::PackageRef()); + SG_CHECK_EQUAL(p1->id(), "b737-NG"); + + // but not listed + auto packs = root->allPackages(); + auto k = std::find(packs.begin(), packs.end(), p1); + SG_VERIFY(k == packs.end()); + + // check the new catalog + auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt"); + SG_VERIFY(altCat->isEnabled()); + SG_CHECK_EQUAL(altCat->status(), pkg::Delegate::STATUS_REFRESHED); + SG_CHECK_EQUAL(altCat->id(), "org.flightgear.test.catalog-alt"); + SG_CHECK_EQUAL(altCat->url(), "http://localhost:2000/catalogTest1/catalog-alt.xml"); + + it = std::find(enabledCats.begin(), enabledCats.end(), altCat); + SG_VERIFY(it != enabledCats.end()); + + // install a parallel package from the new catalog + pkg::PackageRef p2 = root->getPackageById("org.flightgear.test.catalog-alt.b737-NG"); + SG_CHECK_EQUAL(p2->id(), "b737-NG"); + pkg::InstallRef ins = p2->install(); + SG_VERIFY(ins->isQueued()); + waitForUpdateComplete(cl, root); + SG_VERIFY(p2->isInstalled()); + + // do a non-scoped lookup, we should get the new one + pkg::PackageRef p3 = root->getPackageById("b737-NG"); + SG_CHECK_EQUAL(p2, p3); + } + + // test that re-init-ing doesn't mirgate again + { + pkg::RootRef root(new pkg::Root(rootPath, "7.5")); + root->setHTTPClient(cl); + + root->refresh(true); + waitForUpdateComplete(cl, root); + + pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1"); + SG_VERIFY(!cat->isEnabled()); + SG_CHECK_EQUAL(cat->status(), pkg::Delegate::FAIL_VERSION); + + auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt"); + SG_VERIFY(altCat->isEnabled()); + + auto packs = root->allPackages(); + SG_CHECK_EQUAL(packs.size(), 4); + } + + // and now switch back to the older version + { + pkg::RootRef root(new pkg::Root(rootPath, "8.1.0")); + root->setHTTPClient(cl); + root->refresh(true); + waitForUpdateComplete(cl, root); + + pkg::CatalogRef cat = root->getCatalogById("org.flightgear.test.catalog1"); + SG_VERIFY(cat->isEnabled()); + SG_CHECK_EQUAL(cat->status(), pkg::Delegate::STATUS_REFRESHED); + + auto altCat = root->getCatalogById("org.flightgear.test.catalog-alt"); + SG_VERIFY(!altCat->isEnabled()); + + // verify the original aircraft is still installed and available + pkg::PackageRef p1 = root->getPackageById("org.flightgear.test.catalog1.b737-NG"); + SG_VERIFY(p1 != pkg::PackageRef()); + SG_CHECK_EQUAL(p1->id(), "b737-NG"); + SG_VERIFY(p1->isInstalled()); + + // verify the alt package is still installed, + pkg::PackageRef p2 = root->getPackageById("org.flightgear.test.catalog-alt.b737-NG"); + SG_VERIFY(p2 != pkg::PackageRef()); + SG_CHECK_EQUAL(p2->id(), "b737-NG"); + SG_VERIFY(p2->isInstalled()); + } +} + void testOfflineMode(HTTP::Client* cl) { global_catalogVersion = 0; @@ -990,6 +1114,8 @@ int main(int argc, char* argv[]) removeInvalidCatalog(&cl); + testVersionMigrateToId(&cl); + SG_LOG(SG_GENERAL, SG_INFO, "Successfully passed all tests!"); return EXIT_SUCCESS; } diff --git a/simgear/package/Root.cxx b/simgear/package/Root.cxx index b207af2c..ac19af07 100644 --- a/simgear/package/Root.cxx +++ b/simgear/package/Root.cxx @@ -414,7 +414,7 @@ Root::Root(const SGPath& aPath, const std::string& aVersion) : if (cat && cat->isEnabled()) { d->catalogs.insert({cat->id(), cat}); } else if (cat) { - SG_LOG(SG_GENERAL, SG_WARN, "Package-Root init: catalog is disabled: " << cat->id()); + SG_LOG(SG_GENERAL, SG_DEBUG, "Package-Root init: catalog is disabled: " << cat->id()); } } // of child directories iteration } @@ -450,6 +450,17 @@ CatalogRef Root::getCatalogById(const std::string& aId) const return it->second; } +CatalogRef Root::getCatalogByUrl(const std::string& aUrl) const +{ + auto it = std::find_if(d->catalogs.begin(), d->catalogs.end(), + [aUrl](const CatalogDict::value_type& v) + { return (v.second->url() == aUrl); }); + if (it == d->catalogs.end()) + return {}; + + return it->second; +} + PackageRef Root::getPackageById(const std::string& aName) const { size_t lastDot = aName.rfind('.'); diff --git a/simgear/package/Root.hxx b/simgear/package/Root.hxx index 70461915..2c598166 100644 --- a/simgear/package/Root.hxx +++ b/simgear/package/Root.hxx @@ -132,6 +132,8 @@ public: CatalogRef getCatalogById(const std::string& aId) const; + CatalogRef getCatalogByUrl(const std::string& aUrl) const; + void scheduleToUpdate(InstallRef aInstall); /** diff --git a/simgear/package/catalogTest1/catalog-alt.xml b/simgear/package/catalogTest1/catalog-alt.xml new file mode 100644 index 00000000..54d2edc2 --- /dev/null +++ b/simgear/package/catalogTest1/catalog-alt.xml @@ -0,0 +1,141 @@ + + + + org.flightgear.test.catalog-alt + Alternate test catalog + http://localhost:2000/catalogTest1/catalog-alt.xml + 4 + + 7.* + + + + alpha + Alpha package + 9 + 593 + + a469c4b837f0521db48616cfe65ac1ea + http://localhost:2000/catalogTest1/alpha.zip + + alpha + + + + c172p + Cessna 172-P + c172p + A plane made by Cessna + 42 + 860 + + cessna + ga + piston + ifr + + + 3 + 4 + 5 + 4 + + + + + exterior + thumb-exterior.png + http://foo.bar.com/thumb-exterior.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + thumb-something.png + http://foo.bar.com/thumb-something.png + + + + c172p-2d-panel + C172 with 2d panel only + + + + c172p-floats + C172 with floats + + + exterior + thumb-exterior-floats.png + http://foo.bar.com/thumb-exterior-floats.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + + c172p-skis + C172 with skis + + + exterior + thumb-exterior-skis.png + http://foo.bar.com/thumb-exterior-skis.png + + + + panel + thumb-panel.png + http://foo.bar.com/thumb-panel.png + + + + ec0e2ffdf98d6a5c05c77445e5447ff5 + http://localhost:2000/catalogTest1/c172p.zip + + + + + b737-NG + Boeing 737 NG + b737NG + A popular twin-engined narrow body jet + 112 + 860 + + boeing + jet + ifr + + + 5 + 5 + 4 + 4 + + + a94ca5704f305b90767f40617d194ed6 + http://localhost:2000/catalogTest1/b737.tar.gz + + + + + dc3 + DC-3 + 9 + 593 + + a469c4b837f0521db48616cfe65ac1ea + http://localhost:2000/catalogTest1/alpha.zip + + dc3 + + diff --git a/simgear/package/catalogTest1/catalog-v2.xml b/simgear/package/catalogTest1/catalog-v2.xml index 75a19c57..fa416115 100644 --- a/simgear/package/catalogTest1/catalog-v2.xml +++ b/simgear/package/catalogTest1/catalog-v2.xml @@ -15,6 +15,12 @@ http://localhost:2000/catalogTest1/catalog-v10.xml + + 7.* + org.flightgear.test.catalog-alt + http://localhost:2000/catalogTest1/catalog-alt.xml + + alpha Alpha package diff --git a/simgear/scene/sky/cloudfield.cxx b/simgear/scene/sky/cloudfield.cxx index 13ee3030..0b313920 100644 --- a/simgear/scene/sky/cloudfield.cxx +++ b/simgear/scene/sky/cloudfield.cxx @@ -1,4 +1,4 @@ -// a layer of 3d clouds + // a layer of 3d clouds // // Written by Harald JOHNSEN, started April 2005. // @@ -78,56 +78,57 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, SGVec3 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))); - + // Always update the altitude transform, as this allows // the clouds to rise and fall smoothly depending on environment updates. altitude_transform->setPosition(osg::Vec3f(0.0f, 0.0f, (float) asl)); - + // Similarly, always determine the effects of the wind 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); osg::Vec3f windosg = field_transform->getAttitude() * wind; field_transform->setPosition(field_transform->getPosition() + windosg); - + if (!wrap) { // If we're not wrapping the cloudfield, then we make no effort to reposition return false; } - + if ((old_pos - osg_pos).length() > fieldSize*2) { // Big movement - reposition centered to current location. field_transform->setPosition(osg_pos); field_transform->setAttitude(orient); old_pos = osg_pos; - } else { + } else if ((old_pos - osg_pos).length() > fieldSize*0.1) { + // Smaller, but non-trivial movement - check if any clouds need to be moved 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 = 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; - + if (pat == 0) { continue; } - + 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)); - + 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; @@ -135,16 +136,16 @@ bool SGCloudField::reposition( const SGVec3f& p, const SGVec3f& up, double lon, 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() + + SGGeod p = SGGeod::fromCart(toSG(field_transform->getPosition() + fta * pat->getPosition())); - addCloudToTree(pat, p, x, y); + addCloudToTree(pat, p, x, y); } - } + } } - + // Render the clouds in order from farthest away layer to nearest one. field_root->getStateSet()->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin"); return true; @@ -161,7 +162,7 @@ SGCloudField::SGCloudField() : osg::StateSet *rootSet = field_root->getOrCreateStateSet(); rootSet->setRenderBinDetails(CLOUDS_BIN, "DepthSortedBin"); rootSet->setAttributeAndModes(getFog()); - + field_transform->addChild(altitude_transform.get()); placed_root = new osg::Group(); altitude_transform->addChild(placed_root); @@ -169,7 +170,7 @@ SGCloudField::SGCloudField() : lodcount = 0; cloudcount = 0; } - + SGCloudField::~SGCloudField() { } @@ -181,7 +182,7 @@ void SGCloudField::clear(void) { ++itr) { removeCloudFromTree(itr->second); } - + cloud_hash.clear(); } @@ -198,7 +199,7 @@ void SGCloudField::applyVisAndLoDRange(void) } } } - + bool SGCloudField::addCloud(float lon, float lat, float alt, int index, osg::ref_ptr geode) { return addCloud(lon, lat, alt, 0.0f, 0.0f, index, geode); } @@ -244,34 +245,34 @@ void SGCloudField::removeCloudFromTree(osg::ref_ptr transform, float lon, float lat, float alt, float x, float y) { - + // Get the base position SGGeod loc = SGGeod::fromDegFt(lon, lat, alt); - addCloudToTree(transform, loc, x, y); + 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 = 90.0 - SG_RADIANS_TO_DEGREES * atan2(y, x); + double crs = 90.0 - SG_RADIANS_TO_DEGREES * atan2(y, x); double dst = sqrt(x*x + y*y); double endcrs; - - SGGeod base_pos = SGGeod::fromGeodFt(loc, 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); - addCloudToTree(transform, loc); + + // 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) { + + +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); @@ -285,17 +286,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))); - - osg::Vec3f osg_pos = toOsg(fieldcenter); - + + osg::Vec3f osg_pos = toOsg(fieldcenter); + field_transform->setPosition(osg_pos); field_transform->setAttitude(orient); old_pos = osg_pos; } - + // Convert position to cloud-coordinates pos = pos - field_transform->getPosition(); - 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 @@ -310,7 +311,7 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t if ((lodnode1->getCenter() - pos).length2() < lod1_range*lod1_range) { // New cloud is within RADIUS_LEVEL_1 of the center of the LOD node. found = true; - } + } } if (!found) { @@ -318,7 +319,7 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t impostornode = new osgSim::Impostor(); impostornode->setImpostorThreshold(impostor_distance); //impostornode->setImpostorThresholdToBound(); - //impostornode->setCenter(pos); + //impostornode->setCenter(pos); placed_root->addChild(impostornode.get()); lodnode1 = (osg::ref_ptr) impostornode; } else { @@ -330,8 +331,8 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t // Now check if there is a second level LOD node at an appropriate distance found = false; - - for (unsigned int j = 0; (!found) && (j < lodnode1->getNumChildren()); j++) { + + for (unsigned int j = 0; (!found) && (j < lodnode1->getNumChildren()); j++) { lodnode = (osg::LOD*) lodnode1->getChild(j); if ((lodnode->getCenter() - pos).length2() < lod2_range*lod2_range) { // We've found the right leaf LOD node @@ -344,37 +345,37 @@ void SGCloudField::addCloudToTree(osg::ref_ptr t lodnode = new osg::LOD(); lodnode1->addChild(lodnode, 0.0f, lod1_range + lod2_range + view_distance + MAX_CLOUD_DEPTH); lodcount++; - } - + } + transform->setPosition(pos); lodnode->addChild(transform.get(), 0.0f, view_distance + MAX_CLOUD_DEPTH); cloudcount++; SG_LOG(SG_ENVIRONMENT, SG_DEBUG, "Impostors: " << impostorcount << - " LoD: " << lodcount << + " LoD: " << lodcount << " Clouds: " << cloudcount); lodnode->dirtyBound(); lodnode1->dirtyBound(); field_root->dirtyBound(); } - + bool SGCloudField::deleteCloud(int identifier) { osg::ref_ptr transform = cloud_hash[identifier]; if (transform == 0) return false; - + removeCloudFromTree(transform); cloud_hash.erase(identifier); return true; } - + bool SGCloudField::repositionCloud(int identifier, float lon, float lat, float alt) { return repositionCloud(identifier, lon, lat, alt, 0.0f, 0.0f); } bool SGCloudField::repositionCloud(int identifier, float lon, float lat, float alt, float x, float y) { osg::ref_ptr transform = cloud_hash[identifier]; - + if (transform == NULL) return false; removeCloudFromTree(transform); @@ -398,4 +399,3 @@ void SGCloudField::updateFog(double visibility, const osg::Vec4f& color) { fog->setColor(color); fog->setDensity(sqrt_m_log01 / visibility); } - diff --git a/simgear/scene/tgdb/ReaderWriterSTG.cxx b/simgear/scene/tgdb/ReaderWriterSTG.cxx index dbd1f991..0418d710 100644 --- a/simgear/scene/tgdb/ReaderWriterSTG.cxx +++ b/simgear/scene/tgdb/ReaderWriterSTG.cxx @@ -313,10 +313,17 @@ struct ReaderWriterSTG::_ModelBin { return false; } + // starting with 2018.3 we will use deltas rather than absolutes as it is more intuitive for the user + // and somewhat easier to visualise + + double detailedRange = atof(options->getPluginStringData("SimGear::LOD_RANGE_DETAILED").c_str()); + double bareRangeDelta = atof(options->getPluginStringData("SimGear::LOD_RANGE_BARE").c_str()); + double roughRangeDelta = atof(options->getPluginStringData("SimGear::LOD_RANGE_ROUGH").c_str()); + // Determine object ranges. Mesh size of 2000mx2000m needs to be accounted for. - _object_range_bare = 1414.0f + atof(options->getPluginStringData("SimGear::LOD_RANGE_BARE").c_str()); - _object_range_rough = 1414.0f + atof(options->getPluginStringData("SimGear::LOD_RANGE_ROUGH").c_str()); - _object_range_detailed = 1414.0f + atof(options->getPluginStringData("SimGear::LOD_RANGE_DETAILED").c_str()); + _object_range_detailed = 1414.0f + detailedRange; + _object_range_bare = _object_range_detailed + bareRangeDelta; + _object_range_rough = _object_range_detailed + roughRangeDelta; SG_LOG(SG_TERRAIN, SG_INFO, "Loading stg file " << absoluteFileName); diff --git a/simgear/scene/tgdb/TreeBin.cxx b/simgear/scene/tgdb/TreeBin.cxx index ee8f4b39..194a15be 100644 --- a/simgear/scene/tgdb/TreeBin.cxx +++ b/simgear/scene/tgdb/TreeBin.cxx @@ -233,9 +233,11 @@ void addTreeToLeafGeode(Geode* geode, const SGVec3f& p, const SGVec3f& t) int imax = 2; if (use_tree_shadows) { imax = 3; } for (int i = 0; i < imax; ++i) { - DrawArrays* primSet = static_cast(geom->getPrimitiveSet(i)); - if(primSet != nullptr) - primSet->setCount(numVerts); + if (i < geom->getNumPrimitiveSets()) { + DrawArrays* primSet = static_cast(geom->getPrimitiveSet(i)); + if (primSet != nullptr) + primSet->setCount(numVerts); + } } } } diff --git a/simgear/structure/subsystem_mgr.cxx b/simgear/structure/subsystem_mgr.cxx index 537649e2..a7294f5f 100644 --- a/simgear/structure/subsystem_mgr.cxx +++ b/simgear/structure/subsystem_mgr.cxx @@ -253,6 +253,7 @@ SGSubsystemGroup::incrementalInit() { // special case this, simplifies the logic below if (_members.empty()) { + _state = State::INIT; return INIT_DONE; } diff --git a/simgear/structure/subsystem_test.cxx b/simgear/structure/subsystem_test.cxx index a1136a78..219a3244 100644 --- a/simgear/structure/subsystem_test.cxx +++ b/simgear/structure/subsystem_test.cxx @@ -314,6 +314,7 @@ void testIncrementalInit() instruments->set_subsystem(radio1); instruments->set_subsystem(radio2); + manager->bind(); for ( ; ; ) { auto status = manager->incrementalInit(); @@ -338,9 +339,34 @@ void testIncrementalInit() SG_VERIFY(d->hasEvent("fake-radio.nav2-will-init")); SG_VERIFY(d->hasEvent("fake-radio.nav2-did-init")); +} +void testEmptyGroup() +{ + // testing the assert described here: + // https://sourceforge.net/p/flightgear/codetickets/2043/ + // when an empty group is inited, we skipped setting the state + + SGSharedPtr manager = new SGSubsystemMgr; + auto d = new RecorderDelegate; + manager->addDelegate(d); + + auto mySub = manager->add(SGSubsystemMgr::POST_FDM); + auto anotherSub = manager->add(SGSubsystemMgr::POST_FDM); + auto instruments = manager->add(SGSubsystemMgr::POST_FDM); + + manager->bind(); + for ( ; ; ) { + auto status = manager->incrementalInit(); + if (status == SGSubsystemMgr::INIT_DONE) + break; + } + manager->postinit(); + + SG_VERIFY(mySub->wasInited); - + SG_VERIFY(d->hasEvent("instruments-will-init")); + SG_VERIFY(d->hasEvent("instruments-did-init")); } void testSuspendResume() @@ -520,6 +546,7 @@ int main(int argc, char* argv[]) testSuspendResume(); testPropertyRoot(); testAddRemoveAfterInit(); + testEmptyGroup(); cout << __FILE__ << ": All tests passed" << endl; return EXIT_SUCCESS;