1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-19 21:53:51 +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)
{
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())
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<osgUtil::CullVisitor*>(&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<osg::Object> 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)

View file

@ -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;

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())
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<osgUtil::CullVisitor*>(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; 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)
{
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<osgUtil::CullVisitor*>(&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<osgUtil::CullVisitor*>(&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; i<vd->getNumEntries(); ++i)
{
ViewData::Entry& entry = vd->getEntry(i);
entry.set(entry.mNode, !static_cast<osgUtil::CullVisitor*>(&nv)->isCulled(entry.mNode->getBoundingBox()));
}
}
else
mRootNode->traverse(nv);
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()));
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<bool> &abort)
void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic<bool> &abort)
{
ensureQuadTreeBuilt();
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)
{

View file

@ -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<bool>& abort);
void preload(View* view, const osg::Vec3f& viewPoint, std::atomic<bool>& abort);
void reportStats(unsigned int frameNumber, osg::Stats* stats);

View file

@ -15,7 +15,7 @@ class MyView : public View
public:
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)

View file

@ -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; i<mEntries.size(); ++i)
@ -69,8 +78,6 @@ void ViewData::reset(unsigned int frame)
// reset index for next frame
mNumEntries = 0;
mChanged = false;
mFrameLastUsed = frame;
}
void ViewData::clear()
@ -80,6 +87,7 @@ void ViewData::clear()
mNumEntries = 0;
mFrameLastUsed = 0;
mChanged = false;
mHasViewPoint = false;
}
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);
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;
}

View file

@ -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<Entry> 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<ViewData> mViewVector;
@ -84,6 +92,8 @@ namespace Terrain
typedef std::map<osg::ref_ptr<osg::Object>, ViewData*> Map;
Map mViews;
float mReuseDistance;
std::deque<ViewData*> mUnusedViews;
osg::ref_ptr<osg::Object> mDefaultViewer;

View file

@ -8,6 +8,7 @@
#include <atomic>
#include <memory>
#include <set>
#include <atomic>
#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<bool>& abort) {}
virtual void preload(View* view, const osg::Vec3f& viewPoint, std::atomic<bool>& abort) {}
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {}