1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-04-16 08:42:23 +00:00

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.
This commit is contained in:
Bo Svensson 2021-10-31 11:59:34 +00:00 committed by GitHub
parent 356e9d7cf0
commit d88d006984
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 64 deletions

View File

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

View File

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

View File

@ -12,12 +12,10 @@ ViewData::ViewData()
, mHasViewPoint(false) , mHasViewPoint(false)
, mWorldUpdateRevision(0) , mWorldUpdateRevision(0)
{ {
} }
ViewData::~ViewData() ViewData::~ViewData()
{ {
} }
void ViewData::copyFrom(const ViewData& other) void ViewData::copyFrom(const ViewData& other)
@ -38,42 +36,17 @@ void ViewData::add(QuadTreeNode *node)
if (index+1 > mEntries.size()) if (index+1 > mEntries.size())
mEntries.resize(index+1); mEntries.resize(index+1);
Entry& entry = mEntries[index]; ViewDataEntry& entry = mEntries[index];
if (entry.set(node)) if (entry.set(node))
mChanged = true; 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) void ViewData::setViewPoint(const osg::Vec3f &viewPoint)
{ {
mViewPoint = viewPoint; mViewPoint = viewPoint;
mHasViewPoint = true; mHasViewPoint = true;
} }
const osg::Vec3f& ViewData::getViewPoint() const
{
return mViewPoint;
}
// NOTE: As a performance optimisation, we cache mRenderingNodes from previous frames here. // 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(). // If this cache becomes invalid (e.g. through mWorldUpdateRevision), we need to use clear() instead of reset().
void ViewData::reset() void ViewData::reset()
@ -110,14 +83,13 @@ bool ViewData::contains(QuadTreeNode *node) const
return false; return false;
} }
ViewData::Entry::Entry() ViewDataEntry::ViewDataEntry()
: mNode(nullptr) : mNode(nullptr)
, mLodFlags(0) , mLodFlags(0)
{ {
} }
bool ViewData::Entry::set(QuadTreeNode *node) bool ViewDataEntry::set(QuadTreeNode *node)
{ {
if (node == mNode) if (node == mNode)
return false; return false;
@ -173,6 +145,7 @@ ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPo
} }
vd->setViewPoint(viewPoint); vd->setViewPoint(viewPoint);
vd->setActiveGrid(activeGrid); vd->setActiveGrid(activeGrid);
vd->setChanged(true);
needsUpdate = true; needsUpdate = true;
} }
} }

View File

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