diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 60b9a3220e..8f61673b34 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -174,6 +174,8 @@ namespace MWWorld public: TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) : mAbort(false) + , mProgress(views.size()) + , mProgressRange(0) , mTerrainViews(views) , mWorld(world) , mPreloadPositions(preloadPositions) @@ -191,7 +193,7 @@ namespace MWWorld for (unsigned int i=0; ireset(); - mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort); + mWorld->preload(mTerrainViews[i], mPreloadPositions[i].first, mPreloadPositions[i].second, mAbort, mProgress[i], mProgressRange); } } @@ -200,8 +202,13 @@ namespace MWWorld mAbort = true; } + int getProgress() const { return !mProgress.empty() ? mProgress[0].load() : 0; } + int getProgressRange() const { return !mProgress.empty() && mProgress[0].load() ? mProgressRange : 0; } + private: std::atomic mAbort; + std::vector> mProgress; + int mProgressRange; std::vector > mTerrainViews; Terrain::World* mWorld; std::vector mPreloadPositions; @@ -328,9 +335,6 @@ namespace MWWorld } mPreloadCells.erase(found); - - if (cell->isExterior() && mTerrainPreloadItem && mTerrainPreloadItem->isDone()) - mTerrainPreloadItem->storeViews(0.0); } } @@ -415,6 +419,38 @@ namespace MWWorld mUnrefQueue = unrefQueue; } + bool CellPreloader::getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp) + { + if (!mTerrainPreloadItem) + return false; + else if (mTerrainPreloadItem->isDone()) + { + mTerrainPreloadItem->storeViews(timestamp); + mTerrainPreloadItem = nullptr; + return false; + } + else + { + progress = mTerrainPreloadItem->getProgress(); + progressRange = mTerrainPreloadItem->getProgressRange(); + return !progress || progress < progressRange; + } + } + + void CellPreloader::abortTerrainPreloadExcept(const osg::Vec3f& exceptPos) + { + if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) + { + const float resetThreshold = ESM::Land::REAL_SIZE; + for (auto pos : mTerrainPreloadPositions) + if ((pos.first-exceptPos).length2() < resetThreshold*resetThreshold) + return; + mTerrainPreloadItem->abort(); + mTerrainPreloadItem->waitTillDone(); + mTerrainPreloadItem = nullptr; + } + } + void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) { if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) @@ -436,8 +472,12 @@ namespace MWWorld } mTerrainPreloadPositions = positions; - mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); - mWorkQueue->addWorkItem(mTerrainPreloadItem); + + if (!positions.empty()) + { + mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); + mWorkQueue->addWorkItem(mTerrainPreloadItem); + } } } diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index ef28170190..386fee2932 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -72,6 +72,9 @@ namespace MWWorld typedef std::pair PositionCellGrid; void setTerrainPreloadPositions(const std::vector& positions); + bool getTerrainPreloadInProgress(int& progress, int& progressRange, double timestamp); + void abortTerrainPreloadExcept(const osg::Vec3f& exceptPos); + private: Resource::ResourceSystem* mResourceSystem; Resource::BulletShapeManager* mBulletShapeManager; diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 1dac18eaa9..f8c16ca370 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -849,15 +849,42 @@ namespace MWWorld if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenOut(0.5); + preloadTerrain(position.asVec3()); + changeCellGrid(x, y, changeEvent); CellStore* current = MWBase::Environment::get().getWorld()->getExterior(x, y); changePlayerCell(current, position, adjustPlayerPos); + checkTerrainLoaded(); + if (changeEvent) MWBase::Environment::get().getWindowManager()->fadeScreenIn(0.5); } + void Scene::checkTerrainLoaded() + { + Loading::Listener* loadingListener = MWBase::Environment::get().getWindowManager()->getLoadingScreen(); + Loading::ScopedLoad load(loadingListener); + int progress = 0, initialProgress = -1, progressRange = 0; + while (mPreloader->getTerrainPreloadInProgress(progress, progressRange, mRendering.getReferenceTime())) + { + if (initialProgress == -1) + { + loadingListener->setLabel("#{sLoadingMessage4}"); + initialProgress = progress; + } + if (progress) + { + loadingListener->setProgressRange(std::max(0, progressRange-initialProgress)); + loadingListener->setProgress(progress-initialProgress); + } + else + loadingListener->setProgress(0); + OpenThreads::Thread::microSleep(5000); + } + } + CellStore* Scene::getCurrentCell () { return mCurrentCell; @@ -1102,6 +1129,7 @@ namespace MWWorld void Scene::preloadTerrain(const osg::Vec3f &pos) { + mPreloader->abortTerrainPreloadExcept(pos); std::vector vec; vec.emplace_back(pos, gridCenterToBounds(getNewGridCenter(pos))); mPreloader->setTerrainPreloadPositions(vec); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 9b8403c389..349dce4239 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -101,6 +101,8 @@ namespace MWWorld void preloadExteriorGrid(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos); void preloadFastTravelDestinations(const osg::Vec3f& playerPos, const osg::Vec3f& predictedPos, std::vector& exteriorPositions); + void checkTerrainLoaded(); + osg::Vec4i gridCenterToBounds(const osg::Vec2i ¢erCell) const; osg::Vec2i getNewGridCenter(const osg::Vec3f &pos, const osg::Vec2i *currentGridCenter = nullptr) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 0817a42263..5f4f9d1cd1 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -243,7 +243,6 @@ namespace MWWorld if (bypass && !mStartCell.empty()) { ESM::Position pos; - if (findExteriorPosition (mStartCell, pos)) { changeToExteriorCell (pos, true); @@ -384,9 +383,9 @@ namespace MWWorld mPlayer->readRecord(reader, type); if (getPlayerPtr().isInCell()) { - mWorldScene->preloadCell(getPlayerPtr().getCell(), true); if (getPlayerPtr().getCell()->isExterior()) mWorldScene->preloadTerrain(getPlayerPtr().getRefData().getPosition().asVec3()); + mWorldScene->preloadCell(getPlayerPtr().getCell(), true); } break; default: diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 40d2c823b8..cdb9e61910 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -509,7 +509,7 @@ View* QuadTreeWorld::createView() return new ViewData; } -void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort) +void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::Vec4i &grid, std::atomic &abort, std::atomic &progress, int& progressTotal) { ensureQuadTreeBuilt(); @@ -519,12 +519,16 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg:: DefaultLodCallback lodCallback(mLodFactor, MIN_SIZE, grid); mRootNode->traverseNodes(vd, viewPoint, &lodCallback, mViewDistance); - const float cellWorldSize = mStorage->getCellWorldSize(); + if (!progressTotal) + for (unsigned int i=0; igetNumEntries(); ++i) + progressTotal += vd->getEntry(i).mNode->getSize(); + const float cellWorldSize = mStorage->getCellWorldSize(); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true); + progress += entry.mNode->getSize(); } vd->markUnchanged(); } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 15ef0634aa..97fcd004d8 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -38,7 +38,7 @@ namespace Terrain virtual void unloadCell(int x, int y); View* createView(); - void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort); + void preload(View* view, const osg::Vec3f& eyePoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange); void storeView(const View* view, double referenceTime); void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index e559321693..dba79994ad 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -147,7 +147,7 @@ namespace Terrain /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. - virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort) {} + virtual void preload(View* view, const osg::Vec3f& viewPoint, const osg::Vec4i &cellgrid, std::atomic& abort, std::atomic& progress, int& progressRange) {} /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. /// @note Not thread safe.