diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 8d0c42940..964aaa9de 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -311,7 +311,6 @@ namespace MWRender else mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug)); - mTerrain->setDefaultViewer(mViewer->getCamera()); mTerrain->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells")); mTerrain->setWorkQueue(mWorkQueue.get()); diff --git a/apps/openmw/mwrender/water.cpp b/apps/openmw/mwrender/water.cpp index fd7261109..afa38905a 100644 --- a/apps/openmw/mwrender/water.cpp +++ b/apps/openmw/mwrender/water.cpp @@ -161,6 +161,23 @@ private: osg::Plane mPlane; }; +/// This callback on the Camera has the effect of a RELATIVE_RF_INHERIT_VIEWPOINT transform mode (which does not exist in OSG). +/// We want to keep the View Point of the parent camera so we will not have to recreate LODs. +class InheritViewPointCallback : public osg::NodeCallback +{ +public: + InheritViewPointCallback() {} + + virtual void operator()(osg::Node* node, osg::NodeVisitor* nv) + { + osgUtil::CullVisitor* cv = static_cast(nv); + osg::ref_ptr modelViewMatrix = new osg::RefMatrix(*cv->getModelViewMatrix()); + cv->popModelViewMatrix(); + cv->pushModelViewMatrix(modelViewMatrix, osg::Transform::ABSOLUTE_RF_INHERIT_VIEWPOINT); + traverse(node, nv); + } +}; + /// Moves water mesh away from the camera slightly if the camera gets too close on the Z axis. /// The offset works around graphics artifacts that occurred with the GL_DEPTH_CLAMP when the camera gets extremely close to the mesh (seen on NVIDIA at least). /// Must be added as a Cull callback. @@ -224,6 +241,7 @@ public: setReferenceFrame(osg::Camera::RELATIVE_RF); setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); setName("RefractionCamera"); + setCullCallback(new InheritViewPointCallback); setCullMask(Mask_Effect|Mask_Scene|Mask_Object|Mask_Static|Mask_Terrain|Mask_Actor|Mask_ParticleSystem|Mask_Sky|Mask_Sun|Mask_Player|Mask_Lighting); setNodeMask(Mask_RenderToTexture); @@ -315,6 +333,7 @@ public: setReferenceFrame(osg::Camera::RELATIVE_RF); setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water")); setName("ReflectionCamera"); + setCullCallback(new InheritViewPointCallback); int reflectionDetail = Settings::Manager::getInt("reflection detail", "Water"); reflectionDetail = std::min(4, std::max(isInterior ? 2 : 0, reflectionDetail)); diff --git a/apps/openmw/mwworld/cellpreloader.cpp b/apps/openmw/mwworld/cellpreloader.cpp index 98e9c7368..77f522ea6 100644 --- a/apps/openmw/mwworld/cellpreloader.cpp +++ b/apps/openmw/mwworld/cellpreloader.cpp @@ -167,6 +167,44 @@ namespace MWWorld std::vector > mPreloadedObjects; }; + class TerrainPreloadItem : public SceneUtil::WorkItem + { + public: + TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) + : mAbort(false) + , mTerrainViews(views) + , mWorld(world) + , mPreloadPositions(preloadPositions) + { + } + + void storeViews(double referenceTime) + { + for (unsigned int i=0; istoreView(mTerrainViews[i], referenceTime); + } + + virtual void doWork() + { + for (unsigned int i=0; ireset(); + mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort); + } + } + + virtual void abort() + { + mAbort = true; + } + + private: + std::atomic mAbort; + std::vector > mTerrainViews; + Terrain::World* mWorld; + std::vector mPreloadPositions; + }; + /// Worker thread item: update the resource system's cache, effectively deleting unused entries. class UpdateCacheItem : public SceneUtil::WorkItem { @@ -288,6 +326,9 @@ namespace MWWorld } mPreloadCells.erase(found); + + if (cell->isExterior() && mTerrainPreloadItem && mTerrainPreloadItem->isDone()) + mTerrainPreloadItem->storeViews(0.0); } } @@ -329,6 +370,12 @@ namespace MWWorld mWorkQueue->addWorkItem(mUpdateCacheItem, true); mLastResourceCacheUpdate = timestamp; } + + if (mTerrainPreloadItem && mTerrainPreloadItem->isDone()) + { + mTerrainPreloadItem->storeViews(timestamp); + mTerrainPreloadItem = nullptr; + } } void CellPreloader::setExpiryDelay(double expiryDelay) @@ -366,38 +413,6 @@ namespace MWWorld mUnrefQueue = unrefQueue; } - class TerrainPreloadItem : public SceneUtil::WorkItem - { - public: - TerrainPreloadItem(const std::vector >& views, Terrain::World* world, const std::vector& preloadPositions) - : mAbort(false) - , mTerrainViews(views) - , mWorld(world) - , mPreloadPositions(preloadPositions) - { - } - - virtual void doWork() - { - for (unsigned int i=0; ipreload(mTerrainViews[i], mPreloadPositions[i], mAbort); - mTerrainViews[i]->reset(0); - } - } - - virtual void abort() - { - mAbort = true; - } - - private: - std::atomic mAbort; - std::vector > mTerrainViews; - Terrain::World* mWorld; - std::vector mPreloadPositions; - }; - void CellPreloader::setTerrainPreloadPositions(const std::vector &positions) { if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone()) @@ -418,8 +433,6 @@ namespace MWWorld mTerrainViews.push_back(mTerrain->createView()); } - // TODO: provide some way of giving the preloaded view to the main thread when we enter the cell - // right now, we just use it to make sure the resources are preloaded mTerrainPreloadPositions = positions; mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions); mWorkQueue->addWorkItem(mTerrainPreloadItem); diff --git a/apps/openmw/mwworld/cellpreloader.hpp b/apps/openmw/mwworld/cellpreloader.hpp index df878a55d..0501a4f9b 100644 --- a/apps/openmw/mwworld/cellpreloader.hpp +++ b/apps/openmw/mwworld/cellpreloader.hpp @@ -31,6 +31,7 @@ namespace MWRender namespace MWWorld { class CellStore; + class TerrainPreloadItem; class CellPreloader { @@ -105,7 +106,7 @@ namespace MWWorld std::vector > mTerrainViews; std::vector mTerrainPreloadPositions; - osg::ref_ptr mTerrainPreloadItem; + osg::ref_ptr mTerrainPreloadItem; osg::ref_ptr mUpdateCacheItem; }; diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index 0c157cd48..141803cf7 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -114,9 +114,10 @@ void QuadTreeNode::traverse(osg::NodeVisitor &nv) if (!hasValidBounds()) return; - ViewData* vd = getView(nv); + bool needsUpdate = true; + ViewData* vd = getView(nv, needsUpdate); - if ((mLodCallback && mLodCallback->isSufficientDetail(this, distance(vd->getEyePoint()))) || !getNumChildren()) + if ((mLodCallback && mLodCallback->isSufficientDetail(this, distance(vd->getViewPoint()))) || !getNumChildren()) vd->add(this, true); else osg::Group::traverse(nv); @@ -142,26 +143,22 @@ ViewDataMap *QuadTreeNode::getViewDataMap() return mViewDataMap; } -ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv) +ViewData* QuadTreeNode::getView(osg::NodeVisitor &nv, bool& needsUpdate) { + ViewData* vd = NULL; if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) { osgUtil::CullVisitor* cv = static_cast(&nv); - ViewData* vd = mViewDataMap->getViewData(cv->getCurrentCamera()); - vd->setEyePoint(nv.getViewPoint()); - return vd; + vd = mViewDataMap->getViewData(cv->getCurrentCamera(), nv.getViewPoint(), needsUpdate); } else // INTERSECTION_VISITOR { + osg::Vec3f viewPoint = nv.getViewPoint(); static osg::ref_ptr dummyObj = new osg::DummyObject; - ViewData* vd = mViewDataMap->getViewData(dummyObj.get()); - ViewData* defaultView = mViewDataMap->getDefaultView(); - if (defaultView->hasEyePoint()) - vd->setEyePoint(defaultView->getEyePoint()); - else - vd->setEyePoint(nv.getEyePoint()); - return vd; + vd = mViewDataMap->getViewData(dummyObj.get(), viewPoint, needsUpdate); + needsUpdate = true; } + return vd; } void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox) diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 336a257fb..9f7c7bbb7 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -85,7 +85,7 @@ namespace Terrain ViewDataMap* getViewDataMap(); /// Create or retrieve a view for the given traversal. - ViewData* getView(osg::NodeVisitor& nv); + ViewData* getView(osg::NodeVisitor& nv, bool& needsUpdate); private: QuadTreeNode* mParent; diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index fcb0f51a7..499e268dc 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -247,7 +247,7 @@ QuadTreeWorld::~QuadTreeWorld() } -void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& eyePoint, bool visible, float maxDist) +void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& viewPoint, bool visible, float maxDist) { if (!node->hasValidBounds()) return; @@ -255,7 +255,7 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR) visible = visible && !static_cast(nv)->isCulled(node->getBoundingBox()); - float dist = node->distance(eyePoint); + float dist = node->distance(viewPoint); if (dist > maxDist) return; @@ -266,7 +266,7 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac else { for (unsigned int i=0; igetNumChildren(); ++i) - traverse(node->getChild(i), vd, nv, lodCallback, eyePoint, visible, maxDist); + traverse(node->getChild(i), vd, nv, lodCallback, viewPoint, visible, maxDist); } } @@ -367,7 +367,8 @@ void loadRenderingNode(ViewData::Entry& entry, ViewData* vd, int vertexLodMod, C void QuadTreeWorld::accept(osg::NodeVisitor &nv) { - if (nv.getVisitorType() != osg::NodeVisitor::CULL_VISITOR && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) + bool isCullVisitor = nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR; + if (!isCullVisitor && nv.getVisitorType() != osg::NodeVisitor::INTERSECTION_VISITOR) { if (nv.getName().find("AcceptedByComponentsTerrainQuadTreeWorld") != std::string::npos) { @@ -382,26 +383,40 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) return; } - ViewData* vd = mRootNode->getView(nv); + bool needsUpdate = false; + ViewData* vd = mRootNode->getView(nv, needsUpdate); - if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR) + if (needsUpdate) { - osgUtil::CullVisitor* cv = static_cast(&nv); - - osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer(); - if (udc && udc->getNumDescriptions() >= 2 && udc->getDescriptions()[0] == "NoTerrainLod") + vd->reset(); + if (isCullVisitor) { - std::istringstream stream(udc->getDescriptions()[1]); - int x,y; - stream >> x; - stream >> y; - traverseToCell(mRootNode.get(), vd, x,y); + osgUtil::CullVisitor* cv = static_cast(&nv); + + osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer(); + if (udc && udc->getNumDescriptions() >= 2 && udc->getDescriptions()[0] == "NoTerrainLod") + { + std::istringstream stream(udc->getDescriptions()[1]); + int x,y; + stream >> x; + stream >> y; + traverseToCell(mRootNode.get(), vd, x,y); + } + else + traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getViewPoint(), true, mViewDistance); } else - traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getViewPoint(), true, mViewDistance); + mRootNode->traverse(nv); + } + else if (isCullVisitor) + { + // view point is the same, but must still update visible status in case the camera has rotated + for (unsigned int i=0; igetNumEntries(); ++i) + { + ViewData::Entry& entry = vd->getEntry(i); + entry.set(entry.mNode, !static_cast(&nv)->isCulled(entry.mNode->getBoundingBox())); + } } - else - mRootNode->traverse(nv); for (unsigned int i=0; igetNumEntries(); ++i) { @@ -416,14 +431,23 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) { mCompositeMapRenderer->setImmediate(static_cast(udc->getUserData())); udc->setUserData(nullptr); + } entry.mRenderingNode->accept(nv); } } - vd->reset(nv.getTraversalNumber()); + if (!isCullVisitor) + vd->reset(); // we can't reuse intersection views in the next frame because they only contain what is touched by the intersection ray. - mRootNode->getViewDataMap()->clearUnusedViews(nv.getTraversalNumber()); + vd->markUnchanged(); + + double referenceTime = nv.getFrameStamp() ? nv.getFrameStamp()->getReferenceTime() : 0.0; + if (referenceTime != 0.0) + { + vd->setLastUsageTimeStamp(referenceTime); + mViewDataMap->clearUnusedViews(referenceTime); + } } void QuadTreeWorld::ensureQuadTreeBuilt() @@ -473,18 +497,30 @@ View* QuadTreeWorld::createView() return new ViewData; } -void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint, std::atomic &abort) +void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic &abort) { ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); - traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), eyePoint, false, mViewDistance); + vd->setViewPoint(viewPoint); + traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), viewPoint, false, mViewDistance); for (unsigned int i=0; igetNumEntries() && !abort; ++i) { ViewData::Entry& entry = vd->getEntry(i); loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get()); } + vd->markUnchanged(); +} + +void QuadTreeWorld::storeView(const View* view, double referenceTime) +{ + osg::ref_ptr dummy = new osg::DummyObject; + const ViewData* vd = static_cast(view); + bool needsUpdate = false; + ViewData* stored = mViewDataMap->getViewData(dummy, vd->getViewPoint(), needsUpdate); + stored->copyFrom(*vd); + stored->setLastUsageTimeStamp(referenceTime); } void QuadTreeWorld::reportStats(unsigned int frameNumber, osg::Stats *stats) @@ -492,11 +528,6 @@ void QuadTreeWorld::reportStats(unsigned int frameNumber, osg::Stats *stats) stats->setAttribute(frameNumber, "Composite", mCompositeMapRenderer->getCompileSetSize()); } -void QuadTreeWorld::setDefaultViewer(osg::Object *obj) -{ - mViewDataMap->setDefaultViewer(obj); -} - void QuadTreeWorld::loadCell(int x, int y) { // fallback behavior only for undefined cells (every other is already handled in quadtree) diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index 0595096e3..349f27b64 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -38,11 +38,10 @@ namespace Terrain View* createView(); void preload(View* view, const osg::Vec3f& eyePoint, std::atomic& abort); + void storeView(const View* view, double referenceTime); void reportStats(unsigned int frameNumber, osg::Stats* stats); - virtual void setDefaultViewer(osg::Object* obj); - private: void ensureQuadTreeBuilt(); diff --git a/components/terrain/terraingrid.cpp b/components/terrain/terraingrid.cpp index a7d43f673..f8ce2019c 100644 --- a/components/terrain/terraingrid.cpp +++ b/components/terrain/terraingrid.cpp @@ -15,7 +15,7 @@ class MyView : public View public: osg::ref_ptr mLoaded; - virtual void reset(unsigned int frame) {} + virtual void reset() {} }; TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask) diff --git a/components/terrain/viewdata.cpp b/components/terrain/viewdata.cpp index 3d09e7ba0..d1870abaa 100644 --- a/components/terrain/viewdata.cpp +++ b/components/terrain/viewdata.cpp @@ -5,9 +5,9 @@ namespace Terrain ViewData::ViewData() : mNumEntries(0) - , mFrameLastUsed(0) + , mLastUsageTimeStamp(0.0) , mChanged(false) - , mHasEyePoint(false) + , mHasViewPoint(false) { } @@ -17,6 +17,15 @@ ViewData::~ViewData() } +void ViewData::copyFrom(const ViewData& other) +{ + mNumEntries = other.mNumEntries; + mEntries = other.mEntries; + mChanged = other.mChanged; + mHasViewPoint = other.mHasViewPoint; + mViewPoint = other.mViewPoint; +} + void ViewData::add(QuadTreeNode *node, bool visible) { unsigned int index = mNumEntries++; @@ -44,23 +53,23 @@ bool ViewData::hasChanged() const return mChanged; } -bool ViewData::hasEyePoint() const +bool ViewData::hasViewPoint() const { - return mHasEyePoint; + return mHasViewPoint; } -void ViewData::setEyePoint(const osg::Vec3f &eye) +void ViewData::setViewPoint(const osg::Vec3f &viewPoint) { - mEyePoint = eye; - mHasEyePoint = true; + mViewPoint = viewPoint; + mHasViewPoint = true; } -const osg::Vec3f& ViewData::getEyePoint() const +const osg::Vec3f& ViewData::getViewPoint() const { - return mEyePoint; + return mViewPoint; } -void ViewData::reset(unsigned int frame) +void ViewData::reset() { // clear any unused entries for (unsigned int i=mNumEntries; ihasViewPoint() && (vd->getViewPoint() - viewPoint).length2() < maxDist*maxDist; +} + +ViewData *ViewDataMap::getViewData(osg::Object *viewer, const osg::Vec3f& viewPoint, bool& needsUpdate) { Map::const_iterator found = mViews.find(viewer); + ViewData* vd = nullptr; if (found == mViews.end()) { - ViewData* vd = createOrReuseView(); + vd = createOrReuseView(); mViews[viewer] = vd; - return vd; } else - return found->second; + vd = found->second; + + if (!suitable(vd, viewPoint, mReuseDistance)) + { + for (Map::const_iterator other = mViews.begin(); other != mViews.end(); ++other) + { + if (suitable(other->second, viewPoint, mReuseDistance) && other->second->getNumEntries()) + { + vd->copyFrom(*other->second); + needsUpdate = false; + return vd; + } + } + vd->setViewPoint(viewPoint); + needsUpdate = true; + } + else + needsUpdate = false; + + return vd; } ViewData *ViewDataMap::createOrReuseView() @@ -140,12 +172,12 @@ ViewData *ViewDataMap::createOrReuseView() } } -void ViewDataMap::clearUnusedViews(unsigned int frame) +void ViewDataMap::clearUnusedViews(double referenceTime) { for (Map::iterator it = mViews.begin(); it != mViews.end(); ) { ViewData* vd = it->second; - if (vd->getFrameLastUsed() + 2 < frame) + if (vd->getLastUsageTimeStamp() + mExpiryDelay < referenceTime) { vd->clear(); mUnusedViews.push_back(vd); @@ -163,15 +195,4 @@ void ViewDataMap::clear() mViewVector.clear(); } -void ViewDataMap::setDefaultViewer(osg::Object *viewer) -{ - mDefaultViewer = viewer; -} - -ViewData* ViewDataMap::getDefaultView() -{ - return getViewData(mDefaultViewer); -} - - } diff --git a/components/terrain/viewdata.hpp b/components/terrain/viewdata.hpp index 53bcf42c0..001627640 100644 --- a/components/terrain/viewdata.hpp +++ b/components/terrain/viewdata.hpp @@ -21,12 +21,14 @@ namespace Terrain void add(QuadTreeNode* node, bool visible); - void reset(unsigned int frame); + void reset(); void clear(); bool contains(QuadTreeNode* node); + void copyFrom(const ViewData& other); + struct Entry { Entry(); @@ -44,49 +46,55 @@ namespace Terrain Entry& getEntry(unsigned int i); - unsigned int getFrameLastUsed() const { return mFrameLastUsed; } + 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; } - bool hasEyePoint() const; + bool hasViewPoint() const; - void setEyePoint(const osg::Vec3f& eye); - const osg::Vec3f& getEyePoint() const; + void setViewPoint(const osg::Vec3f& viewPoint); + const osg::Vec3f& getViewPoint() const; private: std::vector mEntries; unsigned int mNumEntries; - unsigned int mFrameLastUsed; + double mLastUsageTimeStamp; bool mChanged; - osg::Vec3f mEyePoint; - bool mHasEyePoint; + osg::Vec3f mViewPoint; + bool mHasViewPoint; + float mReuseDistance; }; class ViewDataMap : public osg::Referenced { public: - ViewData* getViewData(osg::Object* viewer); + ViewDataMap() + : mReuseDistance(300) // large value should be safe because the visibility of each node is still updated individually for each camera even if the base view was reused. + // this value also serves as a threshold for when a newly loaded LOD gets unloaded again so that if you hover around an LOD transition point the LODs won't keep loading and unloading all the time. + , mExpiryDelay(1.f) + {} + + ViewData* getViewData(osg::Object* viewer, const osg::Vec3f& viewPoint, bool& needsUpdate); ViewData* createOrReuseView(); - void clearUnusedViews(unsigned int frame); + void clearUnusedViews(double referenceTime); void clear(); - void setDefaultViewer(osg::Object* viewer); - - ViewData* getDefaultView(); - private: std::list mViewVector; typedef std::map, ViewData*> Map; Map mViews; - std::deque mUnusedViews; + float mReuseDistance; + float mExpiryDelay; // time in seconds for unused view to be removed - osg::ref_ptr mDefaultViewer; + std::deque mUnusedViews; }; } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index cfb7edda6..0402b8197 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include "defs.hpp" #include "cellborder.hpp" @@ -48,7 +49,7 @@ namespace Terrain virtual ~View() {} /// Reset internal structure so that the next addition to the view will override the previous frame's contents. - virtual void reset(unsigned int frame) = 0; + virtual void reset() = 0; }; /** @@ -81,7 +82,7 @@ namespace Terrain /// @note Thread safe. virtual void clearAssociatedCaches(); - /// Load a terrain cell at maximum LOD and store it in the View for later use. + /// Load a terrain cell and store it in the View for later use. /// @note Thread safe. virtual void cacheCell(View* view, int x, int y) {} @@ -102,13 +103,15 @@ namespace Terrain virtual View* createView() { return nullptr; } /// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads. - virtual void preload(View* view, const osg::Vec3f& eyePoint, std::atomic& abort) {} + + virtual void preload(View* view, const osg::Vec3f& viewPoint, std::atomic& abort) {} + + /// Store a preloaded view into the cache with the intent that the next rendering traversal can use it. + /// @note Not thread safe. + virtual void storeView(const View* view, double referenceTime) {} virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) {} - /// Set the default viewer (usually a Camera), used as viewpoint for any viewers that don't use their own viewpoint. - virtual void setDefaultViewer(osg::Object* obj) {} - virtual void setViewDistance(float distance) {} Storage* getStorage() { return mStorage; }