mirror of https://github.com/OpenMW/openmw.git
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.
234 lines
6.4 KiB
C++
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;
|
|
}
|
|
|
|
}
|