diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 0282eb8de1..7fc0895846 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -278,6 +278,7 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour , mViewDistance(std::numeric_limits::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& 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 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; igetNumEntries(); ++i) { - ViewData::Entry& entry = vd->getEntry(i); + ViewDataEntry& entry = vd->getEntry(i); osg::BoundingBox bb = static_cast(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; igetNumEntries(); ++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(&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; igetNumEntries() && !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(); +} + } diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 3bd606d6c6..9d21d65fc5 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -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 mRootNode; @@ -75,6 +79,7 @@ namespace Terrain float mMinSize; bool mDebugTerrainChunks; std::unique_ptr mDebugChunkManager; + float mRevalidateDistance; }; } diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index e517390b44..ae23f034a8 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -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; } } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 5d814251ea..b7dbc977b1 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -13,6 +13,18 @@ namespace Terrain class QuadTreeNode; + struct ViewDataEntry + { + ViewDataEntry(); + + bool set(QuadTreeNode* node); + + QuadTreeNode* mNode; + + unsigned int mLodFlags; + osg::ref_ptr 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 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 mEntries; + std::vector mEntries; unsigned int mNumEntries; double mLastUsageTimeStamp; bool mChanged;