diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index f54e423f19..42bd0c69b0 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -1198,6 +1198,10 @@ namespace MWRender mUniformNear->set(mNearClip); mUniformFar->set(mViewDistance); + + // Since our fog is not radial yet, we should take FOV in account, otherwise terrain near viewing distance may disappear. + float distanceMult = std::cos(osg::DegreesToRadians(mFieldOfView)/2.f); + mTerrain->setViewDistance(mViewDistance * (distanceMult ? 1.f/distanceMult : 1.f)); } void RenderingManager::updateTextureFiltering() diff --git a/components/terrain/quadtreenode.cpp b/components/terrain/quadtreenode.cpp index f8237306ea..0c157cd484 100644 --- a/components/terrain/quadtreenode.cpp +++ b/components/terrain/quadtreenode.cpp @@ -76,6 +76,30 @@ QuadTreeNode *QuadTreeNode::getNeighbour(Direction dir) return mNeighbours[dir]; } +float QuadTreeNode::distance(const osg::Vec3f& v) const +{ + const osg::BoundingBox& box = getBoundingBox(); + if (box.contains(v)) + return 0; + else + { + osg::Vec3f maxDist(0,0,0); + if (v.x() < box.xMin()) + maxDist.x() = box.xMin() - v.x(); + else if (v.x() > box.xMax()) + maxDist.x() = v.x() - box.xMax(); + if (v.y() < box.yMin()) + maxDist.y() = box.yMin() - v.y(); + else if (v.y() > box.yMax()) + maxDist.y() = v.y() - box.yMax(); + if (v.z() < box.zMin()) + maxDist.z() = box.zMin() - v.z(); + else if (v.z() > box.zMax()) + maxDist.z() = v.z() - box.zMax(); + return maxDist.length(); + } +} + void QuadTreeNode::initNeighbours() { for (int i=0; i<4; ++i) @@ -92,7 +116,7 @@ void QuadTreeNode::traverse(osg::NodeVisitor &nv) ViewData* vd = getView(nv); - if ((mLodCallback && mLodCallback->isSufficientDetail(this, vd->getEyePoint())) || !getNumChildren()) + if ((mLodCallback && mLodCallback->isSufficientDetail(this, distance(vd->getEyePoint()))) || !getNumChildren()) vd->add(this, true); else osg::Group::traverse(nv); diff --git a/components/terrain/quadtreenode.hpp b/components/terrain/quadtreenode.hpp index 618429c5c0..336a257fb8 100644 --- a/components/terrain/quadtreenode.hpp +++ b/components/terrain/quadtreenode.hpp @@ -23,7 +23,7 @@ namespace Terrain public: virtual ~LodCallback() {} - virtual bool isSufficientDetail(QuadTreeNode *node, const osg::Vec3f& eyePoint) = 0; + virtual bool isSufficientDetail(QuadTreeNode *node, float dist) = 0; }; class ViewDataMap; @@ -49,6 +49,8 @@ namespace Terrain child->addParent(this); }; + float distance(const osg::Vec3f& v) const; + /// Returns our direction relative to the parent node, or Root if we are the root node. ChildDirection getDirection() { return mDirection; } diff --git a/components/terrain/quadtreeworld.cpp b/components/terrain/quadtreeworld.cpp index 6a4ad67031..5970732292 100644 --- a/components/terrain/quadtreeworld.cpp +++ b/components/terrain/quadtreeworld.cpp @@ -40,33 +40,6 @@ namespace return targetlevel; } - float distanceToBox(const osg::BoundingBox& box, const osg::Vec3f& v) - { - if (box.contains(v)) - return 0; - else - { - osg::Vec3f maxDist(0,0,0); - - if (v.x() < box.xMin()) - maxDist.x() = box.xMin() - v.x(); - else if (v.x() > box.xMax()) - maxDist.x() = v.x() - box.xMax(); - - if (v.y() < box.yMin()) - maxDist.y() = box.yMin() - v.y(); - else if (v.y() > box.yMax()) - maxDist.y() = v.y() - box.yMax(); - - if (v.z() < box.zMin()) - maxDist.z() = box.zMin() - v.z(); - else if (v.z() > box.zMax()) - maxDist.z() = v.z() - box.zMax(); - - return maxDist.length(); - } - } - } namespace Terrain @@ -81,9 +54,8 @@ public: { } - virtual bool isSufficientDetail(QuadTreeNode* node, const osg::Vec3f& eyePoint) + virtual bool isSufficientDetail(QuadTreeNode* node, float dist) { - float dist = distanceToBox(node->getBoundingBox(), eyePoint); int nativeLodLevel = Log2(static_cast(node->getSize()/mMinSize)); int lodLevel = Log2(static_cast(dist/(Constants::CellSizeInUnits*mMinSize*mFactor))); @@ -254,6 +226,7 @@ QuadTreeWorld::QuadTreeWorld(osg::Group *parent, osg::Group *compileRoot, Resour , mQuadTreeBuilt(false) , mLodFactor(lodFactor) , mVertexLodMod(vertexLodMod) + , mViewDistance(std::numeric_limits::max()) { // No need for culling on the Drawable / Transform level as the quad tree performs the culling already. mChunkManager->setCullingActive(false); @@ -269,7 +242,7 @@ QuadTreeWorld::~QuadTreeWorld() } -void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& eyePoint, bool visible) +void traverse(QuadTreeNode* node, ViewData* vd, osg::NodeVisitor* nv, LodCallback* lodCallback, const osg::Vec3f& eyePoint, bool visible, float maxDist) { if (!node->hasValidBounds()) return; @@ -277,14 +250,18 @@ 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()); - bool stopTraversal = (lodCallback && lodCallback->isSufficientDetail(node, eyePoint)) || !node->getNumChildren(); + float dist = node->distance(eyePoint); + if (dist > maxDist) + return; + + bool stopTraversal = (lodCallback && lodCallback->isSufficientDetail(node, dist)) || !node->getNumChildren(); if (stopTraversal) vd->add(node, visible); else { for (unsigned int i=0; igetNumChildren(); ++i) - traverse(node->getChild(i), vd, nv, lodCallback, eyePoint, visible); + traverse(node->getChild(i), vd, nv, lodCallback, eyePoint, visible, maxDist); } } @@ -416,7 +393,7 @@ void QuadTreeWorld::accept(osg::NodeVisitor &nv) traverseToCell(mRootNode.get(), vd, x,y); } else - traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getViewPoint(), true); + traverse(mRootNode.get(), vd, cv, mRootNode->getLodCallback(), cv->getViewPoint(), true, mViewDistance); } else mRootNode->traverse(nv); @@ -496,7 +473,7 @@ void QuadTreeWorld::preload(View *view, const osg::Vec3f &eyePoint) ensureQuadTreeBuilt(); ViewData* vd = static_cast(view); - traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), eyePoint, false); + traverse(mRootNode.get(), vd, nullptr, mRootNode->getLodCallback(), eyePoint, false, mViewDistance); for (unsigned int i=0; igetNumEntries(); ++i) { diff --git a/components/terrain/quadtreeworld.hpp b/components/terrain/quadtreeworld.hpp index f02f5df4a3..bb09048c28 100644 --- a/components/terrain/quadtreeworld.hpp +++ b/components/terrain/quadtreeworld.hpp @@ -28,6 +28,8 @@ namespace Terrain virtual void enable(bool enabled); + virtual void setViewDistance(float distance) { mViewDistance = distance; } + void cacheCell(View *view, int x, int y); /// @note Not thread safe. virtual void loadCell(int x, int y); @@ -52,6 +54,7 @@ namespace Terrain bool mQuadTreeBuilt; float mLodFactor; int mVertexLodMod; + float mViewDistance; }; } diff --git a/components/terrain/world.hpp b/components/terrain/world.hpp index 4fb724ebbf..83a2655fdd 100644 --- a/components/terrain/world.hpp +++ b/components/terrain/world.hpp @@ -100,6 +100,8 @@ namespace Terrain /// 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; } protected: