From 4140e4ea053718bafd411435a0e101feb1fd1bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Mocquillon?= Date: Mon, 26 Sep 2022 17:51:05 +0200 Subject: [PATCH 1/3] Remove LOD which could never be displayed in a chunk --- apps/openmw/mwrender/objectpaging.cpp | 62 +++++++++++++++++++++------ 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index f28b13b9b3..9009360652 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -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 { public: bool mOptimizeBillboards = true; - float mSqrDistance = 0.f; + LODRange mDistances = { 0.f, 0.f }; osg::Vec3f mViewVector; osg::Node::NodeMask mCopyMask = ~0u; mutable std::vector mNodePath; @@ -158,13 +178,28 @@ namespace MWRender } if (const osg::LOD* lod = dynamic_cast(node)) { - osg::Group* n = new osg::Group; + std::vector, LODRange>> children; for (unsigned int i = 0; i < lod->getNumChildren(); ++i) - if (lod->getMinRange(i) * lod->getMinRange(i) <= mSqrDistance - && mSqrDistance < lod->getMaxRange(i) * lod->getMaxRange(i)) - n->addChild(operator()(lod->getChild(i))); - n->setDataVariance(osg::Object::STATIC); - return n; + if (const auto r = intersection(lod->getRangeList()[i], mDistances); !empty(r)) + children.emplace_back(operator()(lod->getChild(i)), lod->getRangeList()[i]); + if (children.empty()) + return nullptr; + + 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(node)) { @@ -299,7 +334,6 @@ namespace MWRender AnalyzeVisitor(osg::Node::NodeMask analyzeMask) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mCurrentStateSet(nullptr) - , mCurrentDistance(0.f) { setTraversalMask(analyzeMask); } @@ -326,8 +360,7 @@ namespace MWRender if (osg::LOD* lod = dynamic_cast(&node)) { for (unsigned int i = 0; i < lod->getNumChildren(); ++i) - if (lod->getMinRange(i) * lod->getMinRange(i) <= mCurrentDistance - && mCurrentDistance < lod->getMaxRange(i) * lod->getMaxRange(i)) + if (const auto r = intersection(lod->getRangeList()[i], mDistances); !empty(r)) traverse(*lod->getChild(i)); return; } @@ -375,7 +408,7 @@ namespace MWRender Result mResult; osg::StateSet* mCurrentStateSet; StateSetCounter mGlobalStateSetCounter; - float mCurrentDistance; + LODRange mDistances = { 0.f, 0.f }; }; 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. 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.mCurrentDistance = (viewPoint - worldCenter).length2(); float minSize = mMinSize; if (mMinSizeMergeFactor) minSize *= mMinSizeMergeFactor; @@ -634,6 +669,7 @@ namespace MWRender auto emplaced = nodes.emplace(cnode, InstanceList()); if (emplaced.second) { + analyzeVisitor.mDistances = LODRange{ smallestDistanceToChunk, higherDistanceToChunk } / ref.mScale; const_cast(cnode.get()) ->accept( analyzeVisitor); // const-trickery required because there is no const version of NodeVisitor @@ -715,7 +751,7 @@ namespace MWRender : osg::CopyOp::DEEP_COPY_NODES); copyop.mOptimizeBillboards = (size > 1 / 4.f); copyop.mNodePath.push_back(trans); - copyop.mSqrDistance = (viewPoint - pos).length2(); + copyop.mDistances = LODRange{ smallestDistanceToChunk, higherDistanceToChunk } / ref.mScale; copyop.mViewVector = (viewPoint - worldCenter); copyop.copy(cnode, trans); copyop.mNodePath.pop_back(); From 7142bb4ce79af85036e2b88b2d07054d90fcf2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Mocquillon?= Date: Wed, 9 Nov 2022 21:57:17 +0100 Subject: [PATCH 2/3] Select only one level of LOD outside the active grid Do not create group when not needed --- apps/openmw/mwrender/objectpaging.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index 9009360652..3e2278f520 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -186,12 +186,7 @@ namespace MWRender return nullptr; if (children.size() == 1) - { - osg::Group* n = new osg::Group; - n->addChild(children.front().first); - n->setDataVariance(osg::Object::STATIC); - return n; - } + return children.front().first.release(); else { osg::LOD* n = new osg::LOD; @@ -572,7 +567,11 @@ namespace MWRender 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; + const auto higherDistanceToChunk = [&] { + if (!activeGrid) + return smallestDistanceToChunk + 1; + return ((size < 1) ? 5 : 3) * ESM::Land::REAL_SIZE * size + 1; + }(); AnalyzeVisitor analyzeVisitor(copyMask); float minSize = mMinSize; From 9aee876687bb3dddf7e3890c98237e01ec3928a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Mocquillon?= Date: Fri, 11 Nov 2022 20:59:25 +0100 Subject: [PATCH 3/3] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc4ac92339..8db016943f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,7 @@ Bug #7034: Misc items defined in one content file are not treated as keys if another content file uses them as such Bug #7042: Weapon follow animations that immediately follow the hit animations cause multiple hits Bug #7044: Changing a class' services does not affect autocalculated NPCs + Feature #6447: Add LOD support to Object Paging Feature #6933: Support high-resolution cursor textures Feature #6945: Support S3TC-compressed and BGR/BGRA NiPixelData Feature #6979: Add support of loading and displaying LOD assets purely based on their filename extension