1
0
mirror of https://gitlab.com/OpenMW/openmw.git synced 2025-03-28 19:21:04 +00:00

Remove LOD which could never be displayed in a chunk

This commit is contained in:
Cédric Mocquillon 2022-09-26 17:51:05 +02:00
parent 577c9ad5fb
commit 4140e4ea05

View File

@ -113,11 +113,31 @@ namespace MWRender
} }
}; };
namespace
{
using LODRange = osg::LOD::MinMaxPair;
LODRange intersection(const LODRange& left, const LODRange& right)
{
return { std::max(left.first, right.first), std::min(left.second, right.second) };
}
bool empty(const LODRange& r)
{
return r.first >= r.second;
}
LODRange operator/(const LODRange& r, float div)
{
return { r.first / div, r.second / div };
}
}
class CopyOp : public osg::CopyOp class CopyOp : public osg::CopyOp
{ {
public: public:
bool mOptimizeBillboards = true; bool mOptimizeBillboards = true;
float mSqrDistance = 0.f; LODRange mDistances = { 0.f, 0.f };
osg::Vec3f mViewVector; osg::Vec3f mViewVector;
osg::Node::NodeMask mCopyMask = ~0u; osg::Node::NodeMask mCopyMask = ~0u;
mutable std::vector<const osg::Node*> mNodePath; mutable std::vector<const osg::Node*> mNodePath;
@ -158,13 +178,28 @@ namespace MWRender
} }
if (const osg::LOD* lod = dynamic_cast<const osg::LOD*>(node)) if (const osg::LOD* lod = dynamic_cast<const osg::LOD*>(node))
{ {
osg::Group* n = new osg::Group; std::vector<std::pair<osg::ref_ptr<osg::Node>, LODRange>> children;
for (unsigned int i = 0; i < lod->getNumChildren(); ++i) for (unsigned int i = 0; i < lod->getNumChildren(); ++i)
if (lod->getMinRange(i) * lod->getMinRange(i) <= mSqrDistance if (const auto r = intersection(lod->getRangeList()[i], mDistances); !empty(r))
&& mSqrDistance < lod->getMaxRange(i) * lod->getMaxRange(i)) children.emplace_back(operator()(lod->getChild(i)), lod->getRangeList()[i]);
n->addChild(operator()(lod->getChild(i))); if (children.empty())
n->setDataVariance(osg::Object::STATIC); return nullptr;
return n;
if (children.size() == 1)
{
osg::Group* n = new osg::Group;
n->addChild(children.front().first);
n->setDataVariance(osg::Object::STATIC);
return n;
}
else
{
osg::LOD* n = new osg::LOD;
for (const auto& [child, range] : children)
n->addChild(child, range.first, range.second);
n->setDataVariance(osg::Object::STATIC);
return n;
}
} }
if (const osg::Sequence* sq = dynamic_cast<const osg::Sequence*>(node)) if (const osg::Sequence* sq = dynamic_cast<const osg::Sequence*>(node))
{ {
@ -299,7 +334,6 @@ namespace MWRender
AnalyzeVisitor(osg::Node::NodeMask analyzeMask) AnalyzeVisitor(osg::Node::NodeMask analyzeMask)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mCurrentStateSet(nullptr) , mCurrentStateSet(nullptr)
, mCurrentDistance(0.f)
{ {
setTraversalMask(analyzeMask); setTraversalMask(analyzeMask);
} }
@ -326,8 +360,7 @@ namespace MWRender
if (osg::LOD* lod = dynamic_cast<osg::LOD*>(&node)) if (osg::LOD* lod = dynamic_cast<osg::LOD*>(&node))
{ {
for (unsigned int i = 0; i < lod->getNumChildren(); ++i) for (unsigned int i = 0; i < lod->getNumChildren(); ++i)
if (lod->getMinRange(i) * lod->getMinRange(i) <= mCurrentDistance if (const auto r = intersection(lod->getRangeList()[i], mDistances); !empty(r))
&& mCurrentDistance < lod->getMaxRange(i) * lod->getMaxRange(i))
traverse(*lod->getChild(i)); traverse(*lod->getChild(i));
return; return;
} }
@ -375,7 +408,7 @@ namespace MWRender
Result mResult; Result mResult;
osg::StateSet* mCurrentStateSet; osg::StateSet* mCurrentStateSet;
StateSetCounter mGlobalStateSetCounter; StateSetCounter mGlobalStateSetCounter;
float mCurrentDistance; LODRange mDistances = { 0.f, 0.f };
}; };
class DebugVisitor : public osg::NodeVisitor class DebugVisitor : public osg::NodeVisitor
@ -538,8 +571,10 @@ namespace MWRender
// Since ObjectPaging does not handle VisController, we can just ignore both types of nodes. // Since ObjectPaging does not handle VisController, we can just ignore both types of nodes.
constexpr auto copyMask = ~Mask_UpdateVisitor; constexpr auto copyMask = ~Mask_UpdateVisitor;
const auto smallestDistanceToChunk = (size > 1 / 8.f) ? (size * ESM::Land::REAL_SIZE) : 0.f;
const auto higherDistanceToChunk = ((size < 1) ? 5 : 3) * ESM::Land::REAL_SIZE * size + 1;
AnalyzeVisitor analyzeVisitor(copyMask); AnalyzeVisitor analyzeVisitor(copyMask);
analyzeVisitor.mCurrentDistance = (viewPoint - worldCenter).length2();
float minSize = mMinSize; float minSize = mMinSize;
if (mMinSizeMergeFactor) if (mMinSizeMergeFactor)
minSize *= mMinSizeMergeFactor; minSize *= mMinSizeMergeFactor;
@ -634,6 +669,7 @@ namespace MWRender
auto emplaced = nodes.emplace(cnode, InstanceList()); auto emplaced = nodes.emplace(cnode, InstanceList());
if (emplaced.second) if (emplaced.second)
{ {
analyzeVisitor.mDistances = LODRange{ smallestDistanceToChunk, higherDistanceToChunk } / ref.mScale;
const_cast<osg::Node*>(cnode.get()) const_cast<osg::Node*>(cnode.get())
->accept( ->accept(
analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor
@ -715,7 +751,7 @@ namespace MWRender
: osg::CopyOp::DEEP_COPY_NODES); : osg::CopyOp::DEEP_COPY_NODES);
copyop.mOptimizeBillboards = (size > 1 / 4.f); copyop.mOptimizeBillboards = (size > 1 / 4.f);
copyop.mNodePath.push_back(trans); copyop.mNodePath.push_back(trans);
copyop.mSqrDistance = (viewPoint - pos).length2(); copyop.mDistances = LODRange{ smallestDistanceToChunk, higherDistanceToChunk } / ref.mScale;
copyop.mViewVector = (viewPoint - worldCenter); copyop.mViewVector = (viewPoint - worldCenter);
copyop.copy(cnode, trans); copyop.copy(cnode, trans);
copyop.mNodePath.pop_back(); copyop.mNodePath.pop_back();