From a629d48df62e9497728efdfae52d27eb4ff5ee4d Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 19 Jul 2017 13:05:51 +0200 Subject: [PATCH 1/5] Update the facedObject after the camera is updated --- apps/openmw/engine.cpp | 3 +++ apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 6 ++---- apps/openmw/mwworld/worldimp.hpp | 3 ++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/openmw/engine.cpp b/apps/openmw/engine.cpp index b1c1311bc..426300149 100644 --- a/apps/openmw/engine.cpp +++ b/apps/openmw/engine.cpp @@ -709,6 +709,9 @@ void OMW::Engine::go() { mViewer->eventTraversal(); mViewer->updateTraversal(); + + mEnvironment.getWorld()->updateWindowManager(); + mViewer->renderingTraversals(); } diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 691891486..5e76d82eb 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -358,6 +358,8 @@ namespace MWBase virtual void update (float duration, bool paused) = 0; + virtual void updateWindowManager () = 0; + virtual MWWorld::Ptr placeObject (const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount) = 0; ///< copy and place an object into the gameworld at the specified cursor position /// @param object diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 6c9cdb394..c6d37b34c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1624,16 +1624,14 @@ namespace MWWorld if (!paused) doPhysics (duration); + updatePlayer(paused); + mPhysics->debugDraw(); mWorldScene->update (duration, paused); - updateWindowManager (); - updateSoundListener(); - updatePlayer(paused); - mSpellPreloadTimer -= duration; if (mSpellPreloadTimer <= 0.f) { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 6fa7d897a..a15dcaf3d 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -130,7 +130,6 @@ namespace MWWorld Ptr copyObjectToCell(const ConstPtr &ptr, CellStore* cell, ESM::Position pos, int count, bool adjustPos); void updateSoundListener(); - void updateWindowManager (); void updatePlayer(bool paused); void preloadSpells(); @@ -461,6 +460,8 @@ namespace MWWorld virtual void update (float duration, bool paused); + virtual void updateWindowManager (); + virtual MWWorld::Ptr placeObject (const MWWorld::ConstPtr& object, float cursorX, float cursorY, int amount); ///< copy and place an object into the gameworld at the specified cursor position /// @param object From ac78d01b2b3c93223222b1a4697c726aafed22bf Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 19 Jul 2017 13:11:44 +0200 Subject: [PATCH 2/5] Terrain: use the main camera's viewpoint for intersection tests Fixes lag spikes caused by intersection tests loading/unloading terrain pages. --- apps/openmw/mwrender/renderingmanager.cpp | 1 + components/terrain/quadtreenode.cpp | 19 ++++++++++++---- components/terrain/quadtreeworld.cpp | 5 +++++ components/terrain/quadtreeworld.hpp | 2 ++ components/terrain/viewdata.cpp | 27 +++++++++++++++++++++++ components/terrain/viewdata.hpp | 13 +++++++++++ components/terrain/world.hpp | 4 ++++ 7 files changed, 67 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 6ee9b8ae8..4d4a36f6c 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -222,6 +222,7 @@ namespace MWRender mTerrain.reset(new Terrain::QuadTreeWorld(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); else mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile)); + mTerrain->setDefaultViewer(mViewer->getCamera()); mCamera.reset(new Camera(mViewer->getCamera())); diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 438303c27..801cdef5c 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -99,8 +99,10 @@ void QuadTreeNode::traverse(osg::NodeVisitor &nv) if (!hasValidBounds()) return; - if ((mLodCallback && mLodCallback->isSufficientDetail(this, nv.getEyePoint())) || !getNumChildren()) - getView(nv)->add(this, true); + ViewData* vd = getView(nv); + + if ((mLodCallback && mLodCallback->isSufficientDetail(this, vd->getEyePoint())) || !getNumChildren()) + vd->add(this, true); else osg::Group::traverse(nv); } @@ -130,11 +132,20 @@ ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv) if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) { osgUtil::CullVisitor* cv = static_cast(&nv); - return mViewDataMap->getViewData(cv->getCurrentCamera(), true); + ViewData* vd = mViewDataMap->getViewData(cv->getCurrentCamera(), true); + vd->setEyePoint(nv.getEyePoint()); + return vd; } else // INTERSECTION_VISITOR { - return mViewDataMap->getViewData(&nv, (nv.referenceCount() > 0)); // if no referenceCount, the visitor was allocated on the stack + static osg::ref_ptr dummyObj = new osg::DummyObject; + ViewData* vd = mViewDataMap->getViewData(dummyObj.get(), true); + ViewData* defaultView = mViewDataMap->getDefaultView(); + if (defaultView->hasEyePoint()) + vd->setEyePoint(defaultView->getEyePoint()); + else + vd->setEyePoint(nv.getEyePoint()); + return vd; } } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index f2a80d673..f31064805 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -456,5 +456,10 @@ void QuadTreeWorld::reportStats(unsigned int frameNumber, osg::Stats *stats) stats->setAttribute(frameNumber, "Composite", mCompositeMapRenderer->getCompileSetSize()); } +void QuadTreeWorld::setDefaultViewer(osg::Object *obj) +{ + mViewDataMap->setDefaultViewer(obj); +} + } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 8ec75917b..ef33f158e 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -33,6 +33,8 @@ namespace Terrain void reportStats(unsigned int frameNumber, osg::Stats* stats); + virtual void setDefaultViewer(osg::Object* obj); + private: void ensureQuadTreeBuilt(); diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 7b3df56b9..5e16537f4 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -7,6 +7,7 @@ ViewData::ViewData() : mNumEntries(0) , mFrameLastUsed(0) , mChanged(false) + , mHasEyePoint(false) { } @@ -43,6 +44,22 @@ bool ViewData::hasChanged() const return mChanged; } +bool ViewData::hasEyePoint() const +{ + return mHasEyePoint; +} + +void ViewData::setEyePoint(const osg::Vec3f &eye) +{ + mEyePoint = eye; + mHasEyePoint = true; +} + +const osg::Vec3f& ViewData::getEyePoint() const +{ + return mEyePoint; +} + void ViewData::reset(unsigned int frame) { // clear any unused entries @@ -150,5 +167,15 @@ void ViewDataMap::clear() mViewVector.clear(); } +void ViewDataMap::setDefaultViewer(osg::Object *viewer) +{ + mDefaultViewer = viewer; +} + +ViewData* ViewDataMap::getDefaultView() +{ + return getViewData(mDefaultViewer, true); +} + } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 30563d566..e4bfbd10c 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -52,12 +52,19 @@ namespace Terrain /// @return Have any nodes changed since the last frame bool hasChanged() const; + bool hasEyePoint() const; + + void setEyePoint(const osg::Vec3f& eye); + const osg::Vec3f& getEyePoint() const; + private: std::vector mEntries; unsigned int mNumEntries; unsigned int mFrameLastUsed; bool mChanged; osg::ref_ptr mViewer; + osg::Vec3f mEyePoint; + bool mHasEyePoint; }; class ViewDataMap : public osg::Referenced @@ -71,6 +78,10 @@ namespace Terrain void clear(); + void setDefaultViewer(osg::Object* viewer); + + ViewData* getDefaultView(); + private: std::list mViewVector; @@ -78,6 +89,8 @@ namespace Terrain Map mViews; std::deque mUnusedViews; + + osg::ref_ptr mDefaultViewer; }; } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 1eafc8bd7..d0576fbd3 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -14,6 +14,7 @@ namespace osg class Group; class Stats; class Node; + class Object; } namespace Resource @@ -87,6 +88,9 @@ namespace Terrain virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} + /// Set the default viewer (usually a Camera), used as viewpoint for any viewers that don't use their own viewpoint. + virtual void setDefaultViewer(osg::Object* obj) {} + Storage* getStorage() { return mStorage; } protected: From 3e03a0d7bd6d00253997f5f10e3fff0de449df81 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 19 Jul 2017 13:14:18 +0200 Subject: [PATCH 3/5] Terrain: remove ref argument that is now always true --- components/terrain/quadtreenode.cpp | 4 ++-- components/terrain/viewdata.cpp | 10 ++++------ components/terrain/viewdata.hpp | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 801cdef5c..0394adea7 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -132,14 +132,14 @@ ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv) if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) { osgUtil::CullVisitor* cv = static_cast(&nv); - ViewData* vd = mViewDataMap->getViewData(cv->getCurrentCamera(), true); + ViewData* vd = mViewDataMap->getViewData(cv->getCurrentCamera()); vd->setEyePoint(nv.getEyePoint()); return vd; } else // INTERSECTION_VISITOR { static osg::ref_ptr dummyObj = new osg::DummyObject; - ViewData* vd = mViewDataMap->getViewData(dummyObj.get(), true); + ViewData* vd = mViewDataMap->getViewData(dummyObj.get()); ViewData* defaultView = mViewDataMap->getDefaultView(); if (defaultView->hasEyePoint()) vd->setEyePoint(defaultView->getEyePoint()); diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 5e16537f4..5c70f65f2 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -112,14 +112,13 @@ bool ViewData::Entry::set(QuadTreeNode *node, bool visible) } } -ViewData *ViewDataMap::getViewData(osg::Object *viewer, bool ref) +ViewData *ViewDataMap::getViewData(osg::Object *viewer) { Map::const_iterator found = mViews.find(viewer); if (found == mViews.end()) { ViewData* vd = createOrReuseView(); - if (ref) - vd->setViewer(viewer); + vd->setViewer(viewer); mViews[viewer] = vd; return vd; } @@ -147,8 +146,7 @@ void ViewDataMap::clearUnusedViews(unsigned int frame) for (Map::iterator it = mViews.begin(); it != mViews.end(); ) { ViewData* vd = it->second; - if ((!vd->getViewer() // if no ref was held, always need to clear to avoid holding a dangling ref. - || vd->getFrameLastUsed() + 2 < frame)) + if (vd->getFrameLastUsed() + 2 < frame) { vd->setViewer(NULL); vd->clear(); @@ -174,7 +172,7 @@ void ViewDataMap::setDefaultViewer(osg::Object *viewer) ViewData* ViewDataMap::getDefaultView() { - return getViewData(mDefaultViewer, true); + return getViewData(mDefaultViewer); } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index e4bfbd10c..aaf5b788f 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -70,7 +70,7 @@ namespace Terrain class ViewDataMap : public osg::Referenced { public: - ViewData* getViewData(osg::Object* viewer, bool ref); + ViewData* getViewData(osg::Object* viewer); ViewData* createOrReuseView(); From 20606a2aff2c4226355ba0e2e4721016ddb2204f Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 19 Jul 2017 16:43:29 +0200 Subject: [PATCH 4/5] Add 'prediction time' setting for cell/terrain pre-loading --- apps/openmw/mwworld/scene.cpp | 3 ++- apps/openmw/mwworld/scene.hpp | 1 + docs/source/reference/modding/settings/cells.rst | 13 +++++++++++++ files/settings-default.cfg | 3 +++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index bec053bc0..8da01cc4b 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -478,6 +478,7 @@ namespace MWWorld , mPreloadExteriorGrid(Settings::Manager::getBool("preload exterior grid", "Cells")) , mPreloadDoors(Settings::Manager::getBool("preload doors", "Cells")) , mPreloadFastTravel(Settings::Manager::getBool("preload fast travel", "Cells")) + , mPredictionTime(Settings::Manager::getFloat("prediction time", "Cells")) { mPreloader.reset(new CellPreloader(rendering.getResourceSystem(), physics->getShapeManager(), rendering.getTerrain(), rendering.getLandManager())); mPreloader->setWorkQueue(mRendering.getWorkQueue()); @@ -685,7 +686,7 @@ namespace MWWorld const MWWorld::ConstPtr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); osg::Vec3f playerPos = player.getRefData().getPosition().asVec3(); osg::Vec3f moved = playerPos - mLastPlayerPos; - osg::Vec3f predictedPos = playerPos + moved / dt; + osg::Vec3f predictedPos = playerPos + moved / dt * mPredictionTime; if (mCurrentCell->isExterior()) exteriorPositions.push_back(predictedPos); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 586ac4f67..e2fac6438 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -67,6 +67,7 @@ namespace MWWorld bool mPreloadExteriorGrid; bool mPreloadDoors; bool mPreloadFastTravel; + float mPredictionTime; osg::Vec3f mLastPlayerPos; diff --git a/docs/source/reference/modding/settings/cells.rst b/docs/source/reference/modding/settings/cells.rst index bbde2fc32..d2e7cf5be 100644 --- a/docs/source/reference/modding/settings/cells.rst +++ b/docs/source/reference/modding/settings/cells.rst @@ -155,6 +155,19 @@ preload cell expiry delay The amount of time (in seconds) that a preloaded cell will stay in cache after it is no longer referenced or required, for example, after the player has moved away from a door without entering it. +prediction time +--------------- + +:Type: floating point +:Range: >=0 +:Default: 1 + +The amount of time (in seconds) in the future to predict the player position for. This predicted position is used to preload any cells and/or distant terrain required at that position. + +This setting will only have an effect if 'preload enabled' is set or the 'distant terrain' in the Terrain section is set. + +Increasing this setting from its default may help if your computer/hard disk is too slow to preload in time and you see loading screens and/or lag spikes. + cache expiry delay ------------------ diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 9c3667a6e..40c1ed099 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -73,6 +73,9 @@ preload cell cache max = 20 # How long to keep preloaded cells in cache after they're no longer referenced/required (in seconds) preload cell expiry delay = 5 +# The predicted position of the player N seconds in the future will be used for preloading cells and distant terrain +prediction time = 1 + # How long to keep models/textures/collision shapes in cache after they're no longer referenced/required (in seconds) cache expiry delay = 5 From 7f634514a833ce346a4b2077593fd15e482779b7 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 19 Jul 2017 17:43:15 +0200 Subject: [PATCH 5/5] Fix the viewport of the character preview being set a frame ahead due to a threading issue --- apps/openmw/mwrender/characterpreview.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index b0899f47e..877c35bba 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -254,7 +254,10 @@ namespace MWRender sizeX = std::max(sizeX, 0); sizeY = std::max(sizeY, 0); - mCamera->setViewport(0, mSizeY-sizeY, std::min(mSizeX, sizeX), std::min(mSizeY, sizeY)); + // NB Camera::setViewport has threading issues + osg::ref_ptr stateset = new osg::StateSet; + stateset->setAttributeAndModes(new osg::Viewport(0, mSizeY-sizeY, std::min(mSizeX, sizeX), std::min(mSizeY, sizeY))); + mCamera->setStateSet(stateset); redraw(); }