mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-20 15:40:32 +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:
parent
356e9d7cf0
commit
d88d006984
@ -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…
x
Reference in New Issue
Block a user