1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-03-29 03:36:40 +00:00

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)
This commit is contained in:
bzzt 2019-02-20 13:37:00 +00:00 committed by Andrei Kortunov
parent 5508153d52
commit 63ab7345be
9 changed files with 136 additions and 65 deletions

View file

@ -382,7 +382,7 @@ namespace MWWorld
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i) for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i)
{ {
mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort); mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort);
mTerrainViews[i]->reset(0); mTerrainViews[i]->reset();
} }
} }

View file

@ -114,9 +114,10 @@ void QuadTreeNode::traverse(osg::NodeVisitor &nv)
if (!hasValidBounds()) if (!hasValidBounds())
return; 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); vd->add(this, true);
else else
osg::Group::traverse(nv); osg::Group::traverse(nv);
@ -142,26 +143,24 @@ ViewDataMap *QuadTreeNode::getViewDataMap()
return mViewDataMap; 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) if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
{ {
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(&nv); osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(&nv);
ViewData* vd = mViewDataMap->getViewData(cv->getCurrentCamera()); vd = mViewDataMap->getViewData(cv->getCurrentCamera(), nv.getViewPoint(), needsUpdate);
vd->setEyePoint(nv.getViewPoint());
return vd;
} }
else // INTERSECTION_VISITOR else // INTERSECTION_VISITOR
{ {
osg::Vec3f viewPoint = nv.getViewPoint();
mViewDataMap->getDefaultViewPoint(viewPoint);
static osg::ref_ptr<osg::Object> dummyObj = new osg::DummyObject; static osg::ref_ptr<osg::Object> dummyObj = new osg::DummyObject;
ViewData* vd = mViewDataMap->getViewData(dummyObj.get()); vd = mViewDataMap->getViewData(dummyObj.get(), viewPoint, needsUpdate);
ViewData* defaultView = mViewDataMap->getDefaultView(); needsUpdate = true;
if (defaultView->hasEyePoint())
vd->setEyePoint(defaultView->getEyePoint());
else
vd->setEyePoint(nv.getEyePoint());
return vd;
} }
return vd;
} }
void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox)

View file

@ -85,7 +85,7 @@ namespace Terrain
ViewDataMap* getViewDataMap(); ViewDataMap* getViewDataMap();
/// Create or retrieve a view for the given traversal. /// Create or retrieve a view for the given traversal.
ViewData* getView(osg::NodeVisitor& nv); ViewData* getView(osg::NodeVisitor& nv, bool& needsUpdate);
private: private:
QuadTreeNode* mParent; QuadTreeNode* mParent;

View file

@ -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()) if (!node->hasValidBounds())
return; return;
@ -255,7 +255,7 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac
if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
visible = visible && !static_cast<osgUtil::CullVisitor*>(nv)->isCulled(node->getBoundingBox()); visible = visible && !static_cast<osgUtil::CullVisitor*>(nv)->isCulled(node->getBoundingBox());
float dist = node->distance(eyePoint); float dist = node->distance(viewPoint);
if (dist > maxDist) if (dist > maxDist)
return; return;
@ -266,7 +266,7 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac
else else
{ {
for (unsigned int i=0; i<node->getNumChildren(); ++i) for (unsigned int i=0; i<node->getNumChildren(); ++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) 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) if (nv.getName().find("AcceptedByComponentsTerrainQuadTreeWorld") != std::string::npos)
{ {
@ -382,9 +383,13 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv)
return; return;
} }
ViewData* vd = mRootNode->getView(nv); bool needsUpdate = false;
ViewData* vd = mRootNode->getView(nv, needsUpdate);
if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) if (needsUpdate)
{
vd->reset();
if (isCullVisitor)
{ {
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(&nv); osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(&nv);
@ -402,6 +407,16 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv)
} }
else else
mRootNode->traverse(nv); 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; i<vd->getNumEntries(); ++i)
{
ViewData::Entry& entry = vd->getEntry(i);
entry.set(entry.mNode, !static_cast<osgUtil::CullVisitor*>(&nv)->isCulled(entry.mNode->getBoundingBox()));
}
}
for (unsigned int i=0; i<vd->getNumEntries(); ++i) for (unsigned int i=0; i<vd->getNumEntries(); ++i)
{ {
@ -416,13 +431,16 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv)
{ {
mCompositeMapRenderer->setImmediate(static_cast<CompositeMap*>(udc->getUserData())); mCompositeMapRenderer->setImmediate(static_cast<CompositeMap*>(udc->getUserData()));
udc->setUserData(nullptr); udc->setUserData(nullptr);
} }
entry.mRenderingNode->accept(nv); 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()); mRootNode->getViewDataMap()->clearUnusedViews(nv.getTraversalNumber());
} }
@ -473,12 +491,13 @@ View* QuadTreeWorld::createView()
return new ViewData; return new ViewData;
} }
void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint, std::atomic<bool> &abort) void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic<bool> &abort)
{ {
ensureQuadTreeBuilt(); ensureQuadTreeBuilt();
ViewData* vd = static_cast<ViewData*>(view); ViewData* vd = static_cast<ViewData*>(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; i<vd->getNumEntries() && !abort; ++i) for (unsigned int i=0; i<vd->getNumEntries() && !abort; ++i)
{ {

View file

@ -37,7 +37,8 @@ namespace Terrain
virtual void unloadCell(int x, int y); virtual void unloadCell(int x, int y);
View* createView(); View* createView();
void preload(View* view, const osg::Vec3f& eyePoint, std::atomic<bool>& abort);
void preload(View* view, const osg::Vec3f& viewPoint, std::atomic<bool>& abort);
void reportStats(unsigned int frameNumber, osg::Stats* stats); void reportStats(unsigned int frameNumber, osg::Stats* stats);

View file

@ -15,7 +15,7 @@ class MyView : public View
public: public:
osg::ref_ptr<osg::Node> mLoaded; osg::ref_ptr<osg::Node> 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) TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask)

View file

@ -7,7 +7,7 @@ ViewData::ViewData()
: mNumEntries(0) : mNumEntries(0)
, mFrameLastUsed(0) , mFrameLastUsed(0)
, mChanged(false) , 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) void ViewData::add(QuadTreeNode *node, bool visible)
{ {
unsigned int index = mNumEntries++; unsigned int index = mNumEntries++;
@ -44,23 +53,23 @@ bool ViewData::hasChanged() const
return mChanged; 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; mViewPoint = viewPoint;
mHasEyePoint = true; 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 // clear any unused entries
for (unsigned int i=mNumEntries; i<mEntries.size(); ++i) for (unsigned int i=mNumEntries; i<mEntries.size(); ++i)
@ -69,8 +78,6 @@ void ViewData::reset(unsigned int frame)
// reset index for next frame // reset index for next frame
mNumEntries = 0; mNumEntries = 0;
mChanged = false; mChanged = false;
mFrameLastUsed = frame;
} }
void ViewData::clear() void ViewData::clear()
@ -80,6 +87,7 @@ void ViewData::clear()
mNumEntries = 0; mNumEntries = 0;
mFrameLastUsed = 0; mFrameLastUsed = 0;
mChanged = false; mChanged = false;
mHasViewPoint = false;
} }
bool ViewData::contains(QuadTreeNode *node) bool ViewData::contains(QuadTreeNode *node)
@ -112,17 +120,42 @@ bool ViewData::Entry::set(QuadTreeNode *node, bool visible)
} }
} }
ViewData *ViewDataMap::getViewData(osg::Object *viewer) bool suitable(ViewData* vd, const osg::Vec3f& viewPoint, float& maxDist)
{
return vd->hasViewPoint() && (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); Map::const_iterator found = mViews.find(viewer);
ViewData* vd = nullptr;
if (found == mViews.end()) if (found == mViews.end())
{ {
ViewData* vd = createOrReuseView(); vd = createOrReuseView();
vd->setViewer(viewer);
mViews[viewer] = vd; mViews[viewer] = vd;
return vd;
} }
else 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() ViewData *ViewDataMap::createOrReuseView()
@ -168,9 +201,16 @@ void ViewDataMap::setDefaultViewer(osg::Object *viewer)
mDefaultViewer = 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;
} }

View file

@ -21,12 +21,14 @@ namespace Terrain
void add(QuadTreeNode* node, bool visible); void add(QuadTreeNode* node, bool visible);
void reset(unsigned int frame); void reset();
void clear(); void clear();
bool contains(QuadTreeNode* node); bool contains(QuadTreeNode* node);
void copyFrom(const ViewData& other);
struct Entry struct Entry
{ {
Entry(); Entry();
@ -45,28 +47,34 @@ namespace Terrain
Entry& getEntry(unsigned int i); Entry& getEntry(unsigned int i);
unsigned int getFrameLastUsed() const { return mFrameLastUsed; } unsigned int getFrameLastUsed() const { return mFrameLastUsed; }
void finishFrame(unsigned int frame) { mFrameLastUsed = frame; mChanged = false; }
/// @return Have any nodes changed since the last frame /// @return Have any nodes changed since the last frame
bool hasChanged() const; bool hasChanged() const;
bool hasEyePoint() const; bool hasViewPoint() const;
void setEyePoint(const osg::Vec3f& eye); void setViewPoint(const osg::Vec3f& viewPoint);
const osg::Vec3f& getEyePoint() const; const osg::Vec3f& getViewPoint() const;
private: private:
std::vector<Entry> mEntries; std::vector<Entry> mEntries;
unsigned int mNumEntries; unsigned int mNumEntries;
unsigned int mFrameLastUsed; unsigned int mFrameLastUsed;
bool mChanged; bool mChanged;
osg::Vec3f mEyePoint; osg::Vec3f mViewPoint;
bool mHasEyePoint; bool mHasViewPoint;
float mReuseDistance;
}; };
class ViewDataMap : public osg::Referenced class ViewDataMap : public osg::Referenced
{ {
public: 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(); ViewData* createOrReuseView();
@ -76,7 +84,7 @@ namespace Terrain
void setDefaultViewer(osg::Object* viewer); void setDefaultViewer(osg::Object* viewer);
ViewData* getDefaultView(); bool getDefaultViewPoint(osg::Vec3f& viewPoint);
private: private:
std::list<ViewData> mViewVector; std::list<ViewData> mViewVector;
@ -84,6 +92,8 @@ namespace Terrain
typedef std::map<osg::ref_ptr<osg::Object>, ViewData*> Map; typedef std::map<osg::ref_ptr<osg::Object>, ViewData*> Map;
Map mViews; Map mViews;
float mReuseDistance;
std::deque<ViewData*> mUnusedViews; std::deque<ViewData*> mUnusedViews;
osg::ref_ptr<osg::Object> mDefaultViewer; osg::ref_ptr<osg::Object> mDefaultViewer;

View file

@ -8,6 +8,7 @@
#include <atomic> #include <atomic>
#include <memory> #include <memory>
#include <set> #include <set>
#include <atomic>
#include "defs.hpp" #include "defs.hpp"
#include "cellborder.hpp" #include "cellborder.hpp"
@ -48,7 +49,7 @@ namespace Terrain
virtual ~View() {} virtual ~View() {}
/// Reset internal structure so that the next addition to the view will override the previous frame's contents. /// 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; } virtual View* createView() { return nullptr; }
/// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. /// @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<bool>& abort) {}
virtual void preload(View* view, const osg::Vec3f& viewPoint, std::atomic<bool>& abort) {}
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {}