You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw/components/terrain/viewdata.cpp

234 lines
6.4 KiB
C++

#include "viewdata.hpp"
#include "quadtreenode.hpp"
#include <algorithm>
namespace Terrain
{
ViewData::ViewData()
: mNumEntries(0)
, mLastUsageTimeStamp(0.0)
, mChanged(false)
, mHasViewPoint(false)
, mWorldUpdateRevision(0)
{
}
ViewData::~ViewData() {}
void ViewData::copyFrom(const ViewData& other)
{
mNumEntries = other.mNumEntries;
mEntries = other.mEntries;
mChanged = other.mChanged;
mHasViewPoint = other.mHasViewPoint;
mViewPoint = other.mViewPoint;
mActiveGrid = other.mActiveGrid;
mWorldUpdateRevision = other.mWorldUpdateRevision;
mNodes = other.mNodes;
}
void ViewData::add(QuadTreeNode* node)
{
unsigned int index = mNumEntries++;
if (index + 1 > mEntries.size())
mEntries.resize(index + 1);
ViewDataEntry& entry = mEntries[index];
if (entry.set(node))
{
mChanged = true;
mNodes.clear();
}
}
void ViewData::setViewPoint(const osg::Vec3f& viewPoint)
{
mViewPoint = viewPoint;
mHasViewPoint = true;
}
// NOTE: As a performance optimisation, we cache mRenderingNodes from previous frames here.
// If this cache becomes invalid (e.g. through mWorldUpdateRevision), we need to use clear() instead of reset().
void ViewData::reset()
{
// clear any unused entries
for (unsigned int i = mNumEntries; i < mEntries.size(); ++i)
mEntries[i].set(nullptr);
// reset index for next frame
mNumEntries = 0;
mChanged = false;
mNodes.clear();
}
void ViewData::clear()
{
for (unsigned int i = 0; i < mEntries.size(); ++i)
mEntries[i].set(nullptr);
mNumEntries = 0;
mLastUsageTimeStamp = 0;
mChanged = false;
mHasViewPoint = false;
mNodes.clear();
}
bool ViewData::suitableToUse(const osg::Vec4i& activeGrid) const
{
return hasViewPoint() && activeGrid == mActiveGrid && getNumEntries();
}
bool ViewData::contains(const QuadTreeNode* node) const
{
return std::binary_search(mNodes.begin(), mNodes.end(), node);
}
void ViewData::buildNodeIndex()
{
if (!mNodes.empty())
return;
mNodes.reserve(mNumEntries);
for (std::size_t i = 0; i < mNumEntries; ++i)
if (const QuadTreeNode* node = mEntries[i].mNode)
mNodes.push_back(node);
std::sort(mNodes.begin(), mNodes.end());
}
void ViewData::removeNodeFromIndex(const QuadTreeNode* node)
{
const auto it = std::lower_bound(mNodes.begin(), mNodes.end(), node);
if (it == mNodes.end() || *it != node)
return;
mNodes.erase(it);
}
ViewDataEntry::ViewDataEntry()
: mNode(nullptr)
, mLodFlags(0)
{
}
bool ViewDataEntry::set(QuadTreeNode* node)
{
if (node == mNode)
return false;
else
{
mNode = node;
// clear cached data
mRenderingNode = nullptr;
return true;
}
}
ViewData* ViewDataMap::getViewData(
osg::Object* viewer, const osg::Vec3f& viewPoint, const osg::Vec4i& activeGrid, bool& needsUpdate)
{
ViewerMap::const_iterator found = mViewers.find(viewer);
ViewData* vd = nullptr;
if (found == mViewers.end())
{
vd = createOrReuseView();
mViewers[viewer] = vd;
}
else
vd = found->second;
needsUpdate = false;
if (!(vd->suitableToUse(activeGrid)
&& (vd->getViewPoint() - viewPoint).length2() < mReuseDistance * mReuseDistance
&& vd->getWorldUpdateRevision() >= mWorldUpdateRevision))
{
float shortestDist = viewer ? mReuseDistance * mReuseDistance : std::numeric_limits<float>::max();
const ViewData* mostSuitableView = nullptr;
for (const ViewData* other : mUsedViews)
{
if (other->suitableToUse(activeGrid) && other->getWorldUpdateRevision() >= mWorldUpdateRevision)
{
float dist = (viewPoint - other->getViewPoint()).length2();
if (dist < shortestDist)
{
shortestDist = dist;
mostSuitableView = other;
}
}
}
if (mostSuitableView && mostSuitableView != vd)
{
vd->copyFrom(*mostSuitableView);
return vd;
}
else if (!mostSuitableView)
{
if (vd->getWorldUpdateRevision() != mWorldUpdateRevision)
{
vd->setWorldUpdateRevision(mWorldUpdateRevision);
vd->clear();
}
vd->setViewPoint(viewPoint);
vd->setActiveGrid(activeGrid);
needsUpdate = true;
}
}
return vd;
}
ViewData* ViewDataMap::createOrReuseView()
{
ViewData* vd = nullptr;
if (mUnusedViews.size())
{
vd = mUnusedViews.front();
mUnusedViews.pop_front();
}
else
{
mViewVector.emplace_back();
vd = &mViewVector.back();
}
mUsedViews.push_back(vd);
vd->setWorldUpdateRevision(mWorldUpdateRevision);
return vd;
}
ViewData* ViewDataMap::createIndependentView() const
{
ViewData* vd = new ViewData;
vd->setWorldUpdateRevision(mWorldUpdateRevision);
return vd;
}
void ViewDataMap::clearUnusedViews(double referenceTime)
{
for (ViewerMap::iterator it = mViewers.begin(); it != mViewers.end();)
{
if (it->second->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
mViewers.erase(it++);
else
++it;
}
for (std::deque<ViewData*>::iterator it = mUsedViews.begin(); it != mUsedViews.end();)
{
if ((*it)->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
{
(*it)->clear();
mUnusedViews.push_back(*it);
it = mUsedViews.erase(it);
}
else
++it;
}
}
void ViewDataMap::rebuildViews()
{
++mWorldUpdateRevision;
}
}