From 63ab7345be753c25c9988f7a63c1a5579e6d41be Mon Sep 17 00:00:00 2001 From: bzzt Date: Wed, 20 Feb 2019 13:37:00 +0000 Subject: [PATCH] Reuse traversal result for different traversal with same view point Rename eyePoint to viewPoint to match OSG conventions (eyePoint is the camera position, viewPoint is for LOD handling) --- apps/openmw/mwworld/cellpreloader.cpp | 2 +- components/terrain/quadtreenode.cpp | 25 +++++---- components/terrain/quadtreenode.hpp | 2 +- components/terrain/quadtreeworld.cpp | 61 ++++++++++++++-------- components/terrain/quadtreeworld.hpp | 3 +- components/terrain/terraingrid.cpp | 2 +- components/terrain/viewdata.cpp | 74 +++++++++++++++++++++------ components/terrain/viewdata.hpp | 26 +++++++--- components/terrain/world.hpp | 6 ++- 9 files changed, 136 insertions(+), 65 deletions(-) diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 98e9c73684..e8d7f93c47 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -382,7 +382,7 @@ namespace MWWorld for (unsigned int i=0; ipreload(mTerrainViews[i], mPreloadPositions[i], mAbort); - mTerrainViews[i]->reset(0); + mTerrainViews[i]->reset(); } } diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 0c157cd484..d9fd4cd458 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -114,9 +114,10 @@ void QuadTreeNode::traverse(osg::NodeVisitor &nv) if (!hasValidBounds()) return; - ViewData* vd = getView(nv); + bool needsUpdate = true; + ViewData* vd = getView(nv, needsUpdate); - if ((mLodCallback && mLodCallback->isSufficientDetail(this, distance(vd->getEyePoint()))) || !getNumChildren()) + if ((mLodCallback && mLodCallback->isSufficientDetail(this, distance(vd->getViewPoint()))) || !getNumChildren()) vd->add(this, true); else osg::Group::traverse(nv); @@ -142,26 +143,24 @@ ViewDataMap *QuadTreeNode::getViewDataMap() return mViewDataMap; } -ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv) +ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv, bool& needsUpdate) { + ViewData* vd = NULL; if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) { osgUtil::CullVisitor* cv = static_cast(&nv); - ViewData* vd = mViewDataMap->getViewData(cv->getCurrentCamera()); - vd->setEyePoint(nv.getViewPoint()); - return vd; + vd = mViewDataMap->getViewData(cv->getCurrentCamera(), nv.getViewPoint(), needsUpdate); } else // INTERSECTION_VISITOR { + osg::Vec3f viewPoint = nv.getViewPoint(); + mViewDataMap->getDefaultViewPoint(viewPoint); + static osg::ref_ptr dummyObj = new osg::DummyObject; - ViewData* vd = mViewDataMap->getViewData(dummyObj.get()); - ViewData* defaultView = mViewDataMap->getDefaultView(); - if (defaultView->hasEyePoint()) - vd->setEyePoint(defaultView->getEyePoint()); - else - vd->setEyePoint(nv.getEyePoint()); - return vd; + vd = mViewDataMap->getViewData(dummyObj.get(), viewPoint, needsUpdate); + needsUpdate = true; } + return vd; } void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 336a257fb8..9f7c7bbb7e 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -85,7 +85,7 @@ namespace Terrain ViewDataMap* getViewDataMap(); /// Create or retrieve a view for the given traversal. - ViewData* getView(osg::NodeVisitor& nv); + ViewData* getView(osg::NodeVisitor& nv, bool& needsUpdate); private: QuadTreeNode* mParent; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index fcb0f51a7b..367c825035 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -247,7 +247,7 @@ QuadTreeWorld::~QuadTreeWorld() } -void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& eyePoint, bool visible, float maxDist) +void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& viewPoint, bool visible, float maxDist) { if (!node->hasValidBounds()) return; @@ -255,7 +255,7 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) visible = visible && !static_cast(nv)->isCulled(node->getBoundingBox()); - float dist = node->distance(eyePoint); + float dist = node->distance(viewPoint); if (dist > maxDist) return; @@ -266,7 +266,7 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac else { for (unsigned int i=0; igetNumChildren(); ++i) - traverse(node->getChild(i), vd, nv, lodCallback, eyePoint, visible, maxDist); + traverse(node->getChild(i), vd, nv, lodCallback, viewPoint, visible, maxDist); } } @@ -367,7 +367,8 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, C void QuadTreeWorld::accept(osg::NodeVisitor &nv) { - if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) + bool isCullVisitor = nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR; + if (!isCullVisitor && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) { if (nv.getName().find("AcceptedByComponentsTerrainQuadTreeWorld") != std::string::npos) { @@ -382,26 +383,40 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) return; } - ViewData* vd = mRootNode->getView(nv); + bool needsUpdate = false; + ViewData* vd = mRootNode->getView(nv, needsUpdate); - if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + if (needsUpdate) { - osgUtil::CullVisitor* cv = static_cast(&nv); - - osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer(); - if (udc && udc->getNumDescriptions() >= 2 && udc->getDescriptions()[0] == "NoTerrainLod") + vd->reset(); + if (isCullVisitor) { - std::istringstream stream(udc->getDescriptions()[1]); - int x,y; - stream >> x; - stream >> y; - traverseToCell(mRootNode.get(), vd, x,y); + osgUtil::CullVisitor* cv = static_cast(&nv); + + osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer(); + if (udc && udc->getNumDescriptions() >= 2 && udc->getDescriptions()[0] == "NoTerrainLod") + { + std::istringstream stream(udc->getDescriptions()[1]); + int x,y; + stream >> x; + stream >> y; + traverseToCell(mRootNode.get(), vd, x,y); + } + else + traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getViewPoint(), true, mViewDistance); } else - traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getViewPoint(), true, mViewDistance); + mRootNode->traverse(nv); + } + else if (isCullVisitor) + { + // view point is the same, but must still update visible status in case the camera has rotated + for (unsigned int i=0; igetNumEntries(); ++i) + { + ViewData::Entry& entry = vd->getEntry(i); + entry.set(entry.mNode, !static_cast(&nv)->isCulled(entry.mNode->getBoundingBox())); + } } - else - mRootNode->traverse(nv); for (unsigned int i=0; igetNumEntries(); ++i) { @@ -416,13 +431,16 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) { mCompositeMapRenderer->setImmediate(static_cast(udc->getUserData())); udc->setUserData(nullptr); + } entry.mRenderingNode->accept(nv); } } - vd->reset(nv.getTraversalNumber()); + if (!isCullVisitor) + vd->reset(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. + vd->finishFrame(nv.getTraversalNumber()); mRootNode->getViewDataMap()->clearUnusedViews(nv.getTraversalNumber()); } @@ -473,12 +491,13 @@ View* QuadTreeWorld::createView() return new ViewData; } -void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint, std::atomic &abort) +void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic &abort) { ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); - traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), eyePoint, false, mViewDistance); + vd->setViewPoint(viewPoint); + traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), viewPoint, false, mViewDistance); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 0595096e38..0ee17b35b0 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -37,7 +37,8 @@ namespace Terrain virtual void unloadCell(int x, int y); View* createView(); - void preload(View* view, const osg::Vec3f& eyePoint, std::atomic& abort); + + void preload(View* view, const osg::Vec3f& viewPoint, std::atomic& abort); void reportStats(unsigned int frameNumber, osg::Stats* stats); diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index a7d43f6730..f8ce2019c2 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -15,7 +15,7 @@ class MyView : public View public: osg::ref_ptr mLoaded; - virtual void reset(unsigned int frame) {} + virtual void reset() {} }; TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask) diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 3d09e7ba05..1f15bcc7ff 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -7,7 +7,7 @@ ViewData::ViewData() : mNumEntries(0) , mFrameLastUsed(0) , mChanged(false) - , mHasEyePoint(false) + , mHasViewPoint(false) { } @@ -17,6 +17,15 @@ ViewData::~ViewData() } +void ViewData::copyFrom(const ViewData& other) +{ + mNumEntries = other.mNumEntries; + mEntries = other.mEntries; + mChanged = other.mChanged; + mHasViewPoint = other.mHasViewPoint; + mViewPoint = other.mViewPoint; +} + void ViewData::add(QuadTreeNode *node, bool visible) { unsigned int index = mNumEntries++; @@ -44,23 +53,23 @@ bool ViewData::hasChanged() const return mChanged; } -bool ViewData::hasEyePoint() const +bool ViewData::hasViewPoint() const { - return mHasEyePoint; + return mHasViewPoint; } -void ViewData::setEyePoint(const osg::Vec3f &eye) +void ViewData::setViewPoint(const osg::Vec3f &viewPoint) { - mEyePoint = eye; - mHasEyePoint = true; + mViewPoint = viewPoint; + mHasViewPoint = true; } -const osg::Vec3f& ViewData::getEyePoint() const +const osg::Vec3f& ViewData::getViewPoint() const { - return mEyePoint; + return mViewPoint; } -void ViewData::reset(unsigned int frame) +void ViewData::reset() { // clear any unused entries for (unsigned int i=mNumEntries; ihasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist; +} + +ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, bool& needsUpdate) { Map::const_iterator found = mViews.find(viewer); + ViewData* vd = nullptr; if (found == mViews.end()) { - ViewData* vd = createOrReuseView(); + vd = createOrReuseView(); + vd->setViewer(viewer); mViews[viewer] = vd; - return vd; } else - return found->second; + vd = found->second; + + if (!suitable(vd, viewPoint, mReuseDistance)) + { + for (Map::const_iterator other = mViews.begin(); other != mViews.end(); ++other) + { + if (suitable(other->second, viewPoint, mReuseDistance) && other->second->getNumEntries()) + { + vd->copyFrom(*other->second); + needsUpdate = false; + return vd; + } + } + vd->setViewPoint(viewPoint); + needsUpdate = true; + } + else + needsUpdate = false; + + return vd; } ViewData *ViewDataMap::createOrReuseView() @@ -168,9 +201,16 @@ void ViewDataMap::setDefaultViewer(osg::Object *viewer) mDefaultViewer = viewer; } -ViewData* ViewDataMap::getDefaultView() +bool ViewDataMap::getDefaultViewPoint(osg::Vec3f& viewPoint) { - return getViewData(mDefaultViewer); + Map::const_iterator found = mViews.find(mDefaultViewer); + if (found != mViews.end() && found->second->hasViewPoint()) + { + viewPoint = found->second->getViewPoint(); + return true; + } + else + return false; } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 53bcf42c0c..1d02ace34c 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -21,12 +21,14 @@ namespace Terrain void add(QuadTreeNode* node, bool visible); - void reset(unsigned int frame); + void reset(); void clear(); bool contains(QuadTreeNode* node); + void copyFrom(const ViewData& other); + struct Entry { Entry(); @@ -45,28 +47,34 @@ namespace Terrain Entry& getEntry(unsigned int i); unsigned int getFrameLastUsed() const { return mFrameLastUsed; } + void finishFrame(unsigned int frame) { mFrameLastUsed = frame; mChanged = false; } /// @return Have any nodes changed since the last frame bool hasChanged() const; - bool hasEyePoint() const; + bool hasViewPoint() const; - void setEyePoint(const osg::Vec3f& eye); - const osg::Vec3f& getEyePoint() const; + void setViewPoint(const osg::Vec3f& viewPoint); + const osg::Vec3f& getViewPoint() const; private: std::vector mEntries; unsigned int mNumEntries; unsigned int mFrameLastUsed; bool mChanged; - osg::Vec3f mEyePoint; - bool mHasEyePoint; + osg::Vec3f mViewPoint; + bool mHasViewPoint; + float mReuseDistance; }; class ViewDataMap : public osg::Referenced { public: - ViewData* getViewData(osg::Object* viewer); + ViewDataMap() + : mReuseDistance(100) // large value should be safe because the visibility of each node is still updated individually for each camera even if the base view was reused. + {} + + ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, bool& needsUpdate); ViewData* createOrReuseView(); @@ -76,7 +84,7 @@ namespace Terrain void setDefaultViewer(osg::Object* viewer); - ViewData* getDefaultView(); + bool getDefaultViewPoint(osg::Vec3f& viewPoint); private: std::list mViewVector; @@ -84,6 +92,8 @@ namespace Terrain typedef std::map, ViewData*> Map; Map mViews; + float mReuseDistance; + std::deque mUnusedViews; osg::ref_ptr mDefaultViewer; diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index cfb7edda60..290626069a 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "defs.hpp" #include "cellborder.hpp" @@ -48,7 +49,7 @@ namespace Terrain virtual ~View() {} /// Reset internal structure so that the next addition to the view will override the previous frame's contents. - virtual void reset(unsigned int frame) = 0; + virtual void reset() = 0; }; /** @@ -102,7 +103,8 @@ namespace Terrain virtual View* createView() { return nullptr; } /// @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& eyePoint, std::atomic& abort) {} + + virtual void preload(View* view, const osg::Vec3f& viewPoint, std::atomic& abort) {} virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {}