|
|
|
#include "viewdata.hpp"
|
|
|
|
|
|
|
|
#include "quadtreenode.hpp"
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ViewData::clear()
|
|
|
|
{
|
|
|
|
for (unsigned int i=0; i<mEntries.size(); ++i)
|
|
|
|
mEntries[i].set(nullptr);
|
|
|
|
mNumEntries = 0;
|
|
|
|
mLastUsageTimeStamp = 0;
|
|
|
|
mChanged = false;
|
|
|
|
mHasViewPoint = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ViewData::suitableToUse(const osg::Vec4i &activeGrid) const
|
|
|
|
{
|
|
|
|
return hasViewPoint() && activeGrid == mActiveGrid && getNumEntries();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ViewData::contains(QuadTreeNode *node) const
|
|
|
|
{
|
|
|
|
for (unsigned int i=0; i<mNumEntries; ++i)
|
|
|
|
if (mEntries[i].mNode == node)
|
|
|
|
return true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|