fixes getViewDistance (#3207)

I have been informed by @akortunov that my addition of `Groundcover::getViewDistance` is not working in some cases. Investigations revealed that some `ViewData` code interacting with my additions had been quite thoroughly optimised in a way that was not sufficiently documented and interfered with the new feature. With this PR we repair `getViewDistance` while preserving such optimisations and add a necessary comment to avoid issues in the future. In addition, we now rebuild views when the global `mViewDistance` changes.
pull/3210/head
Bo Svensson 3 years ago committed by GitHub
parent 356e9d7cf0
commit d88d006984
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -278,6 +278,7 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour
, mViewDistance(std::numeric_limits<float>::max())
, mMinSize(1/8.f)
, mDebugTerrainChunks(debugChunks)
, mRevalidateDistance(0.f)
{
mChunkManager->setCompositeMapSize(compMapResolution);
mChunkManager->setCompositeMapLevel(compMapLevel);
@ -346,22 +347,25 @@ unsigned int getLodFlags(QuadTreeNode* node, int ourLod, int vertexLodMod, const
return lodFlags;
}
void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, float cellWorldSize, const osg::Vec4i &gridbounds, const std::vector<QuadTreeWorld::ChunkManager*>& chunkManagers, bool compile, float reuseDistance)
void QuadTreeWorld::loadRenderingNode(ViewDataEntry& entry, ViewData* vd, float cellWorldSize, const osg::Vec4i &gridbounds, bool compile, float reuseDistance)
{
if (!vd->hasChanged() && entry.mRenderingNode)
return;
int ourLod = getVertexLod(entry.mNode, vertexLodMod);
int ourLod = getVertexLod(entry.mNode, mVertexLodMod);
if (vd->hasChanged())
{
// have to recompute the lodFlags in case a neighbour has changed LOD.
unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, vertexLodMod, vd);
unsigned int lodFlags = getLodFlags(entry.mNode, ourLod, mVertexLodMod, vd);
if (lodFlags != entry.mLodFlags)
{
entry.mRenderingNode = nullptr;
entry.mLodFlags = lodFlags;
}
// have to revalidate chunks within a custom view distance.
if (mRevalidateDistance && entry.mNode->distance(vd->getViewPoint()) <= mRevalidateDistance + reuseDistance)
entry.mRenderingNode = nullptr;
}
if (!entry.mRenderingNode)
@ -372,9 +376,9 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, f
const osg::Vec2f& center = entry.mNode->getCenter();
bool activeGrid = (center.x() > gridbounds.x() && center.y() > gridbounds.y() && center.x() < gridbounds.z() && center.y() < gridbounds.w());
for (QuadTreeWorld::ChunkManager* m : chunkManagers)
for (QuadTreeWorld::ChunkManager* m : mChunkManagers)
{
if (m->getViewDistance() && entry.mNode->distance(vd->getViewPoint()) > m->getViewDistance() + reuseDistance)
if (mRevalidateDistance && m->getViewDistance() && entry.mNode->distance(vd->getViewPoint()) > m->getViewDistance() + reuseDistance)
continue;
osg::ref_ptr<osg::Node> n = m->getChunk(entry.mNode->getSize(), entry.mNode->getCenter(), ourLod, entry.mLodFlags, activeGrid, vd->getViewPoint(), compile);
if (n) pat->addChild(n);
@ -398,7 +402,7 @@ void updateWaterCullingView(HeightCullCallback* callback, ViewData* vd, osgUtil:
static bool debug = getenv("OPENMW_WATER_CULLING_DEBUG") != nullptr;
for (unsigned int i=0; i<vd->getNumEntries(); ++i)
{
ViewData::Entry& entry = vd->getEntry(i);
ViewDataEntry& entry = vd->getEntry(i);
osg::BoundingBox bb = static_cast<TerrainDrawable*>(entry.mRenderingNode->asGroup()->getChild(0))->getWaterBoundingBox();
if (!bb.valid())
continue;
@ -457,15 +461,15 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv)
for (unsigned int i=0; i<vd->getNumEntries(); ++i)
{
ViewData::Entry& entry = vd->getEntry(i);
loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, mActiveGrid, mChunkManagers, false, mViewDataMap->getReuseDistance());
ViewDataEntry& entry = vd->getEntry(i);
loadRenderingNode(entry, vd, cellWorldSize, mActiveGrid, false, mViewDataMap->getReuseDistance());
entry.mRenderingNode->accept(nv);
}
if (mHeightCullCallback && isCullVisitor)
updateWaterCullingView(mHeightCullCallback, vd, static_cast<osgUtil::CullVisitor*>(&nv), mStorage->getCellWorldSize(), !isGridEmpty());
vd->markUnchanged();
vd->setChanged(false);
double referenceTime = nv.getFrameStamp() ? nv.getFrameStamp()->getReferenceTime() : 0.0;
if (referenceTime != 0.0)
@ -540,9 +544,9 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, const osg::
const float reuseDistance = std::max(mViewDataMap->getReuseDistance(), std::abs(distanceModifier));
for (unsigned int i=startEntry; i<vd->getNumEntries() && !abort; ++i)
{
ViewData::Entry& entry = vd->getEntry(i);
ViewDataEntry& entry = vd->getEntry(i);
loadRenderingNode(entry, vd, mVertexLodMod, cellWorldSize, grid, mChunkManagers, true, reuseDistance);
loadRenderingNode(entry, vd, cellWorldSize, grid, true, reuseDistance);
if (pass==0) reporter.addProgress(entry.mNode->getSize());
entry.mNode = nullptr; // Clear node lest we break the neighbours search for the next pass
}
@ -579,6 +583,8 @@ void QuadTreeWorld::addChunkManager(QuadTreeWorld::ChunkManager* m)
{
mChunkManagers.push_back(m);
mTerrainRoot->setNodeMask(mTerrainRoot->getNodeMask()|m->getNodeMask());
if (m->getViewDistance())
mRevalidateDistance = std::max(m->getViewDistance(), mRevalidateDistance);
}
void QuadTreeWorld::rebuildViews()
@ -586,4 +592,12 @@ void QuadTreeWorld::rebuildViews()
mViewDataMap->rebuildViews();
}
void QuadTreeWorld::setViewDistance(float viewDistance)
{
if (mViewDistance == viewDistance)
return;
mViewDistance = viewDistance;
mViewDataMap->rebuildViews();
}
}

@ -16,6 +16,9 @@ namespace Terrain
{
class RootNode;
class ViewDataMap;
class ViewData;
struct ViewDataEntry;
class DebugChunkManager;
/// @brief Terrain implementation that loads cells into a Quad Tree, with geometry LOD and texture LOD.
@ -30,7 +33,7 @@ namespace Terrain
void enable(bool enabled) override;
void setViewDistance(float distance) override { mViewDistance = distance; }
void setViewDistance(float distance) override;
void cacheCell(View *view, int x, int y) override {}
/// @note Not thread safe.
@ -60,6 +63,7 @@ namespace Terrain
private:
void ensureQuadTreeBuilt();
void loadRenderingNode(ViewDataEntry& entry, ViewData* vd, float cellWorldSize, const osg::Vec4i &gridbounds, bool compile, float reuseDistance);
osg::ref_ptr<RootNode> mRootNode;
@ -75,6 +79,7 @@ namespace Terrain
float mMinSize;
bool mDebugTerrainChunks;
std::unique_ptr<DebugChunkManager> mDebugChunkManager;
float mRevalidateDistance;
};
}

@ -12,12 +12,10 @@ ViewData::ViewData()
, mHasViewPoint(false)
, mWorldUpdateRevision(0)
{
}
ViewData::~ViewData()
{
}
void ViewData::copyFrom(const ViewData& other)
@ -38,42 +36,17 @@ void ViewData::add(QuadTreeNode *node)
if (index+1 > mEntries.size())
mEntries.resize(index+1);
Entry& entry = mEntries[index];
ViewDataEntry& entry = mEntries[index];
if (entry.set(node))
mChanged = true;
}
unsigned int ViewData::getNumEntries() const
{
return mNumEntries;
}
ViewData::Entry &ViewData::getEntry(unsigned int i)
{
return mEntries[i];
}
bool ViewData::hasChanged() const
{
return mChanged;
}
bool ViewData::hasViewPoint() const
{
return mHasViewPoint;
}
void ViewData::setViewPoint(const osg::Vec3f &viewPoint)
{
mViewPoint = viewPoint;
mHasViewPoint = true;
}
const osg::Vec3f& ViewData::getViewPoint() const
{
return mViewPoint;
}
// 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()
@ -110,14 +83,13 @@ bool ViewData::contains(QuadTreeNode *node) const
return false;
}
ViewData::Entry::Entry()
ViewDataEntry::ViewDataEntry()
: mNode(nullptr)
, mLodFlags(0)
{
}
bool ViewData::Entry::set(QuadTreeNode *node)
bool ViewDataEntry::set(QuadTreeNode *node)
{
if (node == mNode)
return false;
@ -173,6 +145,7 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo
}
vd->setViewPoint(viewPoint);
vd->setActiveGrid(activeGrid);
vd->setChanged(true);
needsUpdate = true;
}
}

@ -13,6 +13,18 @@ namespace Terrain
class QuadTreeNode;
struct ViewDataEntry
{
ViewDataEntry();
bool set(QuadTreeNode* node);
QuadTreeNode* mNode;
unsigned int mLodFlags;
osg::ref_ptr<osg::Node> mRenderingNode;
};
class ViewData : public View
{
public:
@ -31,33 +43,22 @@ namespace Terrain
void copyFrom(const ViewData& other);
struct Entry
{
Entry();
bool set(QuadTreeNode* node);
QuadTreeNode* mNode;
unsigned int mLodFlags;
osg::ref_ptr<osg::Node> mRenderingNode;
};
unsigned int getNumEntries() const;
Entry& getEntry(unsigned int i);
unsigned int getNumEntries() const { return mNumEntries; }
ViewDataEntry& getEntry(unsigned int i) { return mEntries[i]; }
double getLastUsageTimeStamp() const { return mLastUsageTimeStamp; }
void setLastUsageTimeStamp(double timeStamp) { mLastUsageTimeStamp = timeStamp; }
/// @return Have any nodes changed since the last frame
bool hasChanged() const;
void markUnchanged() { mChanged = false; }
/// Indicates at least one mNode of mEntries has changed or the view point has moved beyond mReuseDistance.
/// @note Such changes may necessitate a revalidation of cached mRenderingNodes elsewhere depending
/// on the parameters that affect the creation of mRenderingNode.
bool hasChanged() const { return mChanged; }
void setChanged(bool changed) { mChanged = changed; }
bool hasViewPoint() const;
bool hasViewPoint() const { return mHasViewPoint; }
void setViewPoint(const osg::Vec3f& viewPoint);
const osg::Vec3f& getViewPoint() const;
const osg::Vec3f& getViewPoint() const { return mViewPoint; }
void setActiveGrid(const osg::Vec4i &grid) { if (grid != mActiveGrid) {mActiveGrid = grid;mEntries.clear();mNumEntries=0;} }
const osg::Vec4i &getActiveGrid() const { return mActiveGrid;}
@ -66,7 +67,7 @@ namespace Terrain
void setWorldUpdateRevision(int updateRevision) { mWorldUpdateRevision = updateRevision; }
private:
std::vector<Entry> mEntries;
std::vector<ViewDataEntry> mEntries;
unsigned int mNumEntries;
double mLastUsageTimeStamp;
bool mChanged;

Loading…
Cancel
Save