mirror of
https://github.com/TES3MP/TES3MP.git
synced 2025-09-28 23:51:09 -04:00
commit
f4e113e7c1
@ -311,7 +311,6 @@ namespace MWRender
|
|||||||
else
|
else
|
||||||
mTerrain.reset(new Terrain::TerrainGrid(sceneRoot, mRootNode, mResourceSystem, mTerrainStorage, Mask_Terrain, Mask_PreCompile, Mask_Debug));
|
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->setTargetFrameRate(Settings::Manager::getFloat("target framerate", "Cells"));
|
||||||
mTerrain->setWorkQueue(mWorkQueue.get());
|
mTerrain->setWorkQueue(mWorkQueue.get());
|
||||||
|
|
||||||
|
@ -161,6 +161,23 @@ private:
|
|||||||
osg::Plane mPlane;
|
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<osgUtil::CullVisitor*>(nv);
|
||||||
|
osg::ref_ptr<osg::RefMatrix> 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.
|
/// 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).
|
/// 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.
|
/// Must be added as a Cull callback.
|
||||||
@ -224,6 +241,7 @@ public:
|
|||||||
setReferenceFrame(osg::Camera::RELATIVE_RF);
|
setReferenceFrame(osg::Camera::RELATIVE_RF);
|
||||||
setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
|
setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
|
||||||
setName("RefractionCamera");
|
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);
|
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);
|
setNodeMask(Mask_RenderToTexture);
|
||||||
@ -315,6 +333,7 @@ public:
|
|||||||
setReferenceFrame(osg::Camera::RELATIVE_RF);
|
setReferenceFrame(osg::Camera::RELATIVE_RF);
|
||||||
setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
|
setSmallFeatureCullingPixelSize(Settings::Manager::getInt("small feature culling pixel size", "Water"));
|
||||||
setName("ReflectionCamera");
|
setName("ReflectionCamera");
|
||||||
|
setCullCallback(new InheritViewPointCallback);
|
||||||
|
|
||||||
int reflectionDetail = Settings::Manager::getInt("reflection detail", "Water");
|
int reflectionDetail = Settings::Manager::getInt("reflection detail", "Water");
|
||||||
reflectionDetail = std::min(4, std::max(isInterior ? 2 : 0, reflectionDetail));
|
reflectionDetail = std::min(4, std::max(isInterior ? 2 : 0, reflectionDetail));
|
||||||
|
@ -167,6 +167,44 @@ namespace MWWorld
|
|||||||
std::vector<osg::ref_ptr<const osg::Object> > mPreloadedObjects;
|
std::vector<osg::ref_ptr<const osg::Object> > mPreloadedObjects;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TerrainPreloadItem : public SceneUtil::WorkItem
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TerrainPreloadItem(const std::vector<osg::ref_ptr<Terrain::View> >& views, Terrain::World* world, const std::vector<osg::Vec3f>& preloadPositions)
|
||||||
|
: mAbort(false)
|
||||||
|
, mTerrainViews(views)
|
||||||
|
, mWorld(world)
|
||||||
|
, mPreloadPositions(preloadPositions)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void storeViews(double referenceTime)
|
||||||
|
{
|
||||||
|
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size(); ++i)
|
||||||
|
mWorld->storeView(mTerrainViews[i], referenceTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void doWork()
|
||||||
|
{
|
||||||
|
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i)
|
||||||
|
{
|
||||||
|
mTerrainViews[i]->reset();
|
||||||
|
mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void abort()
|
||||||
|
{
|
||||||
|
mAbort = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic<bool> mAbort;
|
||||||
|
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
|
||||||
|
Terrain::World* mWorld;
|
||||||
|
std::vector<osg::Vec3f> mPreloadPositions;
|
||||||
|
};
|
||||||
|
|
||||||
/// Worker thread item: update the resource system's cache, effectively deleting unused entries.
|
/// Worker thread item: update the resource system's cache, effectively deleting unused entries.
|
||||||
class UpdateCacheItem : public SceneUtil::WorkItem
|
class UpdateCacheItem : public SceneUtil::WorkItem
|
||||||
{
|
{
|
||||||
@ -288,6 +326,9 @@ namespace MWWorld
|
|||||||
}
|
}
|
||||||
|
|
||||||
mPreloadCells.erase(found);
|
mPreloadCells.erase(found);
|
||||||
|
|
||||||
|
if (cell->isExterior() && mTerrainPreloadItem && mTerrainPreloadItem->isDone())
|
||||||
|
mTerrainPreloadItem->storeViews(0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,6 +370,12 @@ namespace MWWorld
|
|||||||
mWorkQueue->addWorkItem(mUpdateCacheItem, true);
|
mWorkQueue->addWorkItem(mUpdateCacheItem, true);
|
||||||
mLastResourceCacheUpdate = timestamp;
|
mLastResourceCacheUpdate = timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (mTerrainPreloadItem && mTerrainPreloadItem->isDone())
|
||||||
|
{
|
||||||
|
mTerrainPreloadItem->storeViews(timestamp);
|
||||||
|
mTerrainPreloadItem = nullptr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellPreloader::setExpiryDelay(double expiryDelay)
|
void CellPreloader::setExpiryDelay(double expiryDelay)
|
||||||
@ -366,38 +413,6 @@ namespace MWWorld
|
|||||||
mUnrefQueue = unrefQueue;
|
mUnrefQueue = unrefQueue;
|
||||||
}
|
}
|
||||||
|
|
||||||
class TerrainPreloadItem : public SceneUtil::WorkItem
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TerrainPreloadItem(const std::vector<osg::ref_ptr<Terrain::View> >& views, Terrain::World* world, const std::vector<osg::Vec3f>& preloadPositions)
|
|
||||||
: mAbort(false)
|
|
||||||
, mTerrainViews(views)
|
|
||||||
, mWorld(world)
|
|
||||||
, mPreloadPositions(preloadPositions)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void doWork()
|
|
||||||
{
|
|
||||||
for (unsigned int i=0; i<mTerrainViews.size() && i<mPreloadPositions.size() && !mAbort; ++i)
|
|
||||||
{
|
|
||||||
mWorld->preload(mTerrainViews[i], mPreloadPositions[i], mAbort);
|
|
||||||
mTerrainViews[i]->reset(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void abort()
|
|
||||||
{
|
|
||||||
mAbort = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::atomic<bool> mAbort;
|
|
||||||
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
|
|
||||||
Terrain::World* mWorld;
|
|
||||||
std::vector<osg::Vec3f> mPreloadPositions;
|
|
||||||
};
|
|
||||||
|
|
||||||
void CellPreloader::setTerrainPreloadPositions(const std::vector<osg::Vec3f> &positions)
|
void CellPreloader::setTerrainPreloadPositions(const std::vector<osg::Vec3f> &positions)
|
||||||
{
|
{
|
||||||
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
|
if (mTerrainPreloadItem && !mTerrainPreloadItem->isDone())
|
||||||
@ -418,8 +433,6 @@ namespace MWWorld
|
|||||||
mTerrainViews.push_back(mTerrain->createView());
|
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;
|
mTerrainPreloadPositions = positions;
|
||||||
mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions);
|
mTerrainPreloadItem = new TerrainPreloadItem(mTerrainViews, mTerrain, positions);
|
||||||
mWorkQueue->addWorkItem(mTerrainPreloadItem);
|
mWorkQueue->addWorkItem(mTerrainPreloadItem);
|
||||||
|
@ -31,6 +31,7 @@ namespace MWRender
|
|||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
class CellStore;
|
class CellStore;
|
||||||
|
class TerrainPreloadItem;
|
||||||
|
|
||||||
class CellPreloader
|
class CellPreloader
|
||||||
{
|
{
|
||||||
@ -105,7 +106,7 @@ namespace MWWorld
|
|||||||
|
|
||||||
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
|
std::vector<osg::ref_ptr<Terrain::View> > mTerrainViews;
|
||||||
std::vector<osg::Vec3f> mTerrainPreloadPositions;
|
std::vector<osg::Vec3f> mTerrainPreloadPositions;
|
||||||
osg::ref_ptr<SceneUtil::WorkItem> mTerrainPreloadItem;
|
osg::ref_ptr<TerrainPreloadItem> mTerrainPreloadItem;
|
||||||
osg::ref_ptr<SceneUtil::WorkItem> mUpdateCacheItem;
|
osg::ref_ptr<SceneUtil::WorkItem> mUpdateCacheItem;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -114,9 +114,10 @@ void QuadTreeNode::traverse(osg::NodeVisitor &nv)
|
|||||||
if (!hasValidBounds())
|
if (!hasValidBounds())
|
||||||
return;
|
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);
|
vd->add(this, true);
|
||||||
else
|
else
|
||||||
osg::Group::traverse(nv);
|
osg::Group::traverse(nv);
|
||||||
@ -142,26 +143,22 @@ ViewDataMap *QuadTreeNode::getViewDataMap()
|
|||||||
return mViewDataMap;
|
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)
|
if (nv.getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
|
||||||
{
|
{
|
||||||
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(&nv);
|
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(&nv);
|
||||||
ViewData* vd = mViewDataMap->getViewData(cv->getCurrentCamera());
|
vd = mViewDataMap->getViewData(cv->getCurrentCamera(), nv.getViewPoint(), needsUpdate);
|
||||||
vd->setEyePoint(nv.getViewPoint());
|
|
||||||
return vd;
|
|
||||||
}
|
}
|
||||||
else // INTERSECTION_VISITOR
|
else // INTERSECTION_VISITOR
|
||||||
{
|
{
|
||||||
|
osg::Vec3f viewPoint = nv.getViewPoint();
|
||||||
static osg::ref_ptr<osg::Object> dummyObj = new osg::DummyObject;
|
static osg::ref_ptr<osg::Object> dummyObj = new osg::DummyObject;
|
||||||
ViewData* vd = mViewDataMap->getViewData(dummyObj.get());
|
vd = mViewDataMap->getViewData(dummyObj.get(), viewPoint, needsUpdate);
|
||||||
ViewData* defaultView = mViewDataMap->getDefaultView();
|
needsUpdate = true;
|
||||||
if (defaultView->hasEyePoint())
|
|
||||||
vd->setEyePoint(defaultView->getEyePoint());
|
|
||||||
else
|
|
||||||
vd->setEyePoint(nv.getEyePoint());
|
|
||||||
return vd;
|
|
||||||
}
|
}
|
||||||
|
return vd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox)
|
void QuadTreeNode::setBoundingBox(const osg::BoundingBox &boundingBox)
|
||||||
|
@ -85,7 +85,7 @@ namespace Terrain
|
|||||||
ViewDataMap* getViewDataMap();
|
ViewDataMap* getViewDataMap();
|
||||||
|
|
||||||
/// Create or retrieve a view for the given traversal.
|
/// Create or retrieve a view for the given traversal.
|
||||||
ViewData* getView(osg::NodeVisitor& nv);
|
ViewData* getView(osg::NodeVisitor& nv, bool& needsUpdate);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QuadTreeNode* mParent;
|
QuadTreeNode* mParent;
|
||||||
|
@ -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())
|
if (!node->hasValidBounds())
|
||||||
return;
|
return;
|
||||||
@ -255,7 +255,7 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac
|
|||||||
if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
|
if (nv && nv->getVisitorType() == osg::NodeVisitor::CULL_VISITOR)
|
||||||
visible = visible && !static_cast<osgUtil::CullVisitor*>(nv)->isCulled(node->getBoundingBox());
|
visible = visible && !static_cast<osgUtil::CullVisitor*>(nv)->isCulled(node->getBoundingBox());
|
||||||
|
|
||||||
float dist = node->distance(eyePoint);
|
float dist = node->distance(viewPoint);
|
||||||
if (dist > maxDist)
|
if (dist > maxDist)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -266,7 +266,7 @@ void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallbac
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (unsigned int i=0; i<node->getNumChildren(); ++i)
|
for (unsigned int i=0; i<node->getNumChildren(); ++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)
|
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)
|
if (nv.getName().find("AcceptedByComponentsTerrainQuadTreeWorld") != std::string::npos)
|
||||||
{
|
{
|
||||||
@ -382,26 +383,40 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv)
|
|||||||
return;
|
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<osgUtil::CullVisitor*>(&nv);
|
vd->reset();
|
||||||
|
if (isCullVisitor)
|
||||||
osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer();
|
|
||||||
if (udc && udc->getNumDescriptions() >= 2 && udc->getDescriptions()[0] == "NoTerrainLod")
|
|
||||||
{
|
{
|
||||||
std::istringstream stream(udc->getDescriptions()[1]);
|
osgUtil::CullVisitor* cv = static_cast<osgUtil::CullVisitor*>(&nv);
|
||||||
int x,y;
|
|
||||||
stream >> x;
|
osg::UserDataContainer* udc = cv->getCurrentCamera()->getUserDataContainer();
|
||||||
stream >> y;
|
if (udc && udc->getNumDescriptions() >= 2 && udc->getDescriptions()[0] == "NoTerrainLod")
|
||||||
traverseToCell(mRootNode.get(), vd, x,y);
|
{
|
||||||
|
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
|
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; i<vd->getNumEntries(); ++i)
|
||||||
|
{
|
||||||
|
ViewData::Entry& entry = vd->getEntry(i);
|
||||||
|
entry.set(entry.mNode, !static_cast<osgUtil::CullVisitor*>(&nv)->isCulled(entry.mNode->getBoundingBox()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
mRootNode->traverse(nv);
|
|
||||||
|
|
||||||
for (unsigned int i=0; i<vd->getNumEntries(); ++i)
|
for (unsigned int i=0; i<vd->getNumEntries(); ++i)
|
||||||
{
|
{
|
||||||
@ -416,14 +431,23 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv)
|
|||||||
{
|
{
|
||||||
mCompositeMapRenderer->setImmediate(static_cast<CompositeMap*>(udc->getUserData()));
|
mCompositeMapRenderer->setImmediate(static_cast<CompositeMap*>(udc->getUserData()));
|
||||||
udc->setUserData(nullptr);
|
udc->setUserData(nullptr);
|
||||||
|
|
||||||
}
|
}
|
||||||
entry.mRenderingNode->accept(nv);
|
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()
|
void QuadTreeWorld::ensureQuadTreeBuilt()
|
||||||
@ -473,18 +497,30 @@ View* QuadTreeWorld::createView()
|
|||||||
return new ViewData;
|
return new ViewData;
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint, std::atomic<bool> &abort)
|
void QuadTreeWorld::preload(View *view, const osg::Vec3f &viewPoint, std::atomic<bool> &abort)
|
||||||
{
|
{
|
||||||
ensureQuadTreeBuilt();
|
ensureQuadTreeBuilt();
|
||||||
|
|
||||||
ViewData* vd = static_cast<ViewData*>(view);
|
ViewData* vd = static_cast<ViewData*>(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; i<vd->getNumEntries() && !abort; ++i)
|
for (unsigned int i=0; i<vd->getNumEntries() && !abort; ++i)
|
||||||
{
|
{
|
||||||
ViewData::Entry& entry = vd->getEntry(i);
|
ViewData::Entry& entry = vd->getEntry(i);
|
||||||
loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get());
|
loadRenderingNode(entry, vd, mVertexLodMod, mChunkManager.get());
|
||||||
}
|
}
|
||||||
|
vd->markUnchanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QuadTreeWorld::storeView(const View* view, double referenceTime)
|
||||||
|
{
|
||||||
|
osg::ref_ptr<osg::Object> dummy = new osg::DummyObject;
|
||||||
|
const ViewData* vd = static_cast<const ViewData*>(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)
|
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());
|
stats->setAttribute(frameNumber, "Composite", mCompositeMapRenderer->getCompileSetSize());
|
||||||
}
|
}
|
||||||
|
|
||||||
void QuadTreeWorld::setDefaultViewer(osg::Object *obj)
|
|
||||||
{
|
|
||||||
mViewDataMap->setDefaultViewer(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
void QuadTreeWorld::loadCell(int x, int y)
|
void QuadTreeWorld::loadCell(int x, int y)
|
||||||
{
|
{
|
||||||
// fallback behavior only for undefined cells (every other is already handled in quadtree)
|
// fallback behavior only for undefined cells (every other is already handled in quadtree)
|
||||||
|
@ -38,11 +38,10 @@ namespace Terrain
|
|||||||
|
|
||||||
View* createView();
|
View* createView();
|
||||||
void preload(View* view, const osg::Vec3f& eyePoint, std::atomic<bool>& abort);
|
void preload(View* view, const osg::Vec3f& eyePoint, std::atomic<bool>& abort);
|
||||||
|
void storeView(const View* view, double referenceTime);
|
||||||
|
|
||||||
void reportStats(unsigned int frameNumber, osg::Stats* stats);
|
void reportStats(unsigned int frameNumber, osg::Stats* stats);
|
||||||
|
|
||||||
virtual void setDefaultViewer(osg::Object* obj);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void ensureQuadTreeBuilt();
|
void ensureQuadTreeBuilt();
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ class MyView : public View
|
|||||||
public:
|
public:
|
||||||
osg::ref_ptr<osg::Node> mLoaded;
|
osg::ref_ptr<osg::Node> 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)
|
TerrainGrid::TerrainGrid(osg::Group* parent, osg::Group* compileRoot, Resource::ResourceSystem* resourceSystem, Storage* storage, int nodeMask, int preCompileMask, int borderMask)
|
||||||
|
@ -5,9 +5,9 @@ namespace Terrain
|
|||||||
|
|
||||||
ViewData::ViewData()
|
ViewData::ViewData()
|
||||||
: mNumEntries(0)
|
: mNumEntries(0)
|
||||||
, mFrameLastUsed(0)
|
, mLastUsageTimeStamp(0.0)
|
||||||
, mChanged(false)
|
, 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)
|
void ViewData::add(QuadTreeNode *node, bool visible)
|
||||||
{
|
{
|
||||||
unsigned int index = mNumEntries++;
|
unsigned int index = mNumEntries++;
|
||||||
@ -44,23 +53,23 @@ bool ViewData::hasChanged() const
|
|||||||
return mChanged;
|
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;
|
mViewPoint = viewPoint;
|
||||||
mHasEyePoint = true;
|
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
|
// clear any unused entries
|
||||||
for (unsigned int i=mNumEntries; i<mEntries.size(); ++i)
|
for (unsigned int i=mNumEntries; i<mEntries.size(); ++i)
|
||||||
@ -69,8 +78,6 @@ void ViewData::reset(unsigned int frame)
|
|||||||
// reset index for next frame
|
// reset index for next frame
|
||||||
mNumEntries = 0;
|
mNumEntries = 0;
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
|
|
||||||
mFrameLastUsed = frame;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewData::clear()
|
void ViewData::clear()
|
||||||
@ -78,8 +85,9 @@ void ViewData::clear()
|
|||||||
for (unsigned int i=0; i<mEntries.size(); ++i)
|
for (unsigned int i=0; i<mEntries.size(); ++i)
|
||||||
mEntries[i].set(nullptr, false);
|
mEntries[i].set(nullptr, false);
|
||||||
mNumEntries = 0;
|
mNumEntries = 0;
|
||||||
mFrameLastUsed = 0;
|
mLastUsageTimeStamp = 0;
|
||||||
mChanged = false;
|
mChanged = false;
|
||||||
|
mHasViewPoint = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ViewData::contains(QuadTreeNode *node)
|
bool ViewData::contains(QuadTreeNode *node)
|
||||||
@ -112,17 +120,41 @@ bool ViewData::Entry::set(QuadTreeNode *node, bool visible)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewData *ViewDataMap::getViewData(osg::Object *viewer)
|
bool suitable(ViewData* vd, const osg::Vec3f& viewPoint, float& maxDist)
|
||||||
|
{
|
||||||
|
return vd->hasViewPoint() && (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);
|
Map::const_iterator found = mViews.find(viewer);
|
||||||
|
ViewData* vd = nullptr;
|
||||||
if (found == mViews.end())
|
if (found == mViews.end())
|
||||||
{
|
{
|
||||||
ViewData* vd = createOrReuseView();
|
vd = createOrReuseView();
|
||||||
mViews[viewer] = vd;
|
mViews[viewer] = vd;
|
||||||
return vd;
|
|
||||||
}
|
}
|
||||||
else
|
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()
|
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(); )
|
for (Map::iterator it = mViews.begin(); it != mViews.end(); )
|
||||||
{
|
{
|
||||||
ViewData* vd = it->second;
|
ViewData* vd = it->second;
|
||||||
if (vd->getFrameLastUsed() + 2 < frame)
|
if (vd->getLastUsageTimeStamp() + mExpiryDelay < referenceTime)
|
||||||
{
|
{
|
||||||
vd->clear();
|
vd->clear();
|
||||||
mUnusedViews.push_back(vd);
|
mUnusedViews.push_back(vd);
|
||||||
@ -163,15 +195,4 @@ void ViewDataMap::clear()
|
|||||||
mViewVector.clear();
|
mViewVector.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ViewDataMap::setDefaultViewer(osg::Object *viewer)
|
|
||||||
{
|
|
||||||
mDefaultViewer = viewer;
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewData* ViewDataMap::getDefaultView()
|
|
||||||
{
|
|
||||||
return getViewData(mDefaultViewer);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -21,12 +21,14 @@ namespace Terrain
|
|||||||
|
|
||||||
void add(QuadTreeNode* node, bool visible);
|
void add(QuadTreeNode* node, bool visible);
|
||||||
|
|
||||||
void reset(unsigned int frame);
|
void reset();
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
bool contains(QuadTreeNode* node);
|
bool contains(QuadTreeNode* node);
|
||||||
|
|
||||||
|
void copyFrom(const ViewData& other);
|
||||||
|
|
||||||
struct Entry
|
struct Entry
|
||||||
{
|
{
|
||||||
Entry();
|
Entry();
|
||||||
@ -44,49 +46,55 @@ namespace Terrain
|
|||||||
|
|
||||||
Entry& getEntry(unsigned int i);
|
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
|
/// @return Have any nodes changed since the last frame
|
||||||
bool hasChanged() const;
|
bool hasChanged() const;
|
||||||
|
void markUnchanged() { mChanged = false; }
|
||||||
|
|
||||||
bool hasEyePoint() const;
|
bool hasViewPoint() const;
|
||||||
|
|
||||||
void setEyePoint(const osg::Vec3f& eye);
|
void setViewPoint(const osg::Vec3f& viewPoint);
|
||||||
const osg::Vec3f& getEyePoint() const;
|
const osg::Vec3f& getViewPoint() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Entry> mEntries;
|
std::vector<Entry> mEntries;
|
||||||
unsigned int mNumEntries;
|
unsigned int mNumEntries;
|
||||||
unsigned int mFrameLastUsed;
|
double mLastUsageTimeStamp;
|
||||||
bool mChanged;
|
bool mChanged;
|
||||||
osg::Vec3f mEyePoint;
|
osg::Vec3f mViewPoint;
|
||||||
bool mHasEyePoint;
|
bool mHasViewPoint;
|
||||||
|
float mReuseDistance;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ViewDataMap : public osg::Referenced
|
class ViewDataMap : public osg::Referenced
|
||||||
{
|
{
|
||||||
public:
|
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();
|
ViewData* createOrReuseView();
|
||||||
|
|
||||||
void clearUnusedViews(unsigned int frame);
|
void clearUnusedViews(double referenceTime);
|
||||||
|
|
||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
void setDefaultViewer(osg::Object* viewer);
|
|
||||||
|
|
||||||
ViewData* getDefaultView();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::list<ViewData> mViewVector;
|
std::list<ViewData> mViewVector;
|
||||||
|
|
||||||
typedef std::map<osg::ref_ptr<osg::Object>, ViewData*> Map;
|
typedef std::map<osg::ref_ptr<osg::Object>, ViewData*> Map;
|
||||||
Map mViews;
|
Map mViews;
|
||||||
|
|
||||||
std::deque<ViewData*> mUnusedViews;
|
float mReuseDistance;
|
||||||
|
float mExpiryDelay; // time in seconds for unused view to be removed
|
||||||
|
|
||||||
osg::ref_ptr<osg::Object> mDefaultViewer;
|
std::deque<ViewData*> mUnusedViews;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <set>
|
#include <set>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
#include "defs.hpp"
|
#include "defs.hpp"
|
||||||
#include "cellborder.hpp"
|
#include "cellborder.hpp"
|
||||||
@ -48,7 +49,7 @@ namespace Terrain
|
|||||||
virtual ~View() {}
|
virtual ~View() {}
|
||||||
|
|
||||||
/// Reset internal structure so that the next addition to the view will override the previous frame's contents.
|
/// 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.
|
/// @note Thread safe.
|
||||||
virtual void clearAssociatedCaches();
|
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.
|
/// @note Thread safe.
|
||||||
virtual void cacheCell(View* view, int x, int y) {}
|
virtual void cacheCell(View* view, int x, int y) {}
|
||||||
|
|
||||||
@ -102,13 +103,15 @@ namespace Terrain
|
|||||||
virtual View* createView() { return nullptr; }
|
virtual View* createView() { return nullptr; }
|
||||||
|
|
||||||
/// @note Thread safe, as long as you do not attempt to load into the same view from multiple threads.
|
/// @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<bool>& abort) {}
|
|
||||||
|
virtual void preload(View* view, const osg::Vec3f& viewPoint, std::atomic<bool>& 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) {}
|
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) {}
|
virtual void setViewDistance(float distance) {}
|
||||||
|
|
||||||
Storage* getStorage() { return mStorage; }
|
Storage* getStorage() { return mStorage; }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user