diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 7d6a84023b..93dbbd9d50 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -148,7 +148,7 @@ namespace MWBase virtual MWWorld::ConstPtr getPlayerConstPtr() const = 0; virtual MWWorld::ESMStore& getStore() = 0; - const MWWorld::ESMStore& getStore() const { return const_cast(this)->getStore(); } + virtual const MWWorld::ESMStore& getStore() const = 0; virtual const std::vector& getESMVersions() const = 0; diff --git a/apps/openmw/mwrender/objectpaging.cpp b/apps/openmw/mwrender/objectpaging.cpp index ee52148801..6799b2358b 100644 --- a/apps/openmw/mwrender/objectpaging.cpp +++ b/apps/openmw/mwrender/objectpaging.cpp @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -40,77 +41,76 @@ namespace MWRender { - - bool typeFilter(int type, bool far) + namespace { - switch (type) + bool typeFilter(int type, bool far) { - case ESM::REC_STAT: - case ESM::REC_ACTI: - case ESM::REC_DOOR: - return true; - case ESM::REC_CONT: - return !far; + switch (type) + { + case ESM::REC_STAT: + case ESM::REC_ACTI: + case ESM::REC_DOOR: + return true; + case ESM::REC_CONT: + return !far; - default: - return false; + default: + return false; + } + } + + std::string getModel(int type, ESM::RefId id, const MWWorld::ESMStore& store) + { + switch (type) + { + case ESM::REC_STAT: + return store.get().searchStatic(id)->mModel; + case ESM::REC_ACTI: + return store.get().searchStatic(id)->mModel; + case ESM::REC_DOOR: + return store.get().searchStatic(id)->mModel; + case ESM::REC_CONT: + return store.get().searchStatic(id)->mModel; + default: + return {}; + } } } - std::string getModel(int type, const ESM::RefId& id, const MWWorld::ESMStore& store) - { - switch (type) - { - case ESM::REC_STAT: - return store.get().searchStatic(id)->mModel; - case ESM::REC_ACTI: - return store.get().searchStatic(id)->mModel; - case ESM::REC_DOOR: - return store.get().searchStatic(id)->mModel; - case ESM::REC_CONT: - return store.get().searchStatic(id)->mModel; - default: - return {}; - } - } - - osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char lod, + osg::ref_ptr ObjectPaging::getChunk(float size, const osg::Vec2f& center, unsigned char /*lod*/, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) { - lod = static_cast(lodFlags >> (4 * 4)); if (activeGrid && !mActiveGrid) return nullptr; - ChunkId id = std::make_tuple(center, size, activeGrid); + const ChunkId id = std::make_tuple(center, size, activeGrid); - osg::ref_ptr obj = mCache->getRefFromObjectCache(id); - if (obj) + if (const osg::ref_ptr obj = mCache->getRefFromObjectCache(id)) return static_cast(obj.get()); - else - { - osg::ref_ptr node = createChunk(size, center, activeGrid, viewPoint, compile, lod); - mCache->addEntryToObjectCache(id, node.get()); - return node; - } - } - class CanOptimizeCallback : public SceneUtil::Optimizer::IsOperationPermissibleForObjectCallback - { - public: - bool isOperationPermissibleForObjectImplementation( - const SceneUtil::Optimizer* optimizer, const osg::Drawable* node, unsigned int option) const override - { - return true; - } - bool isOperationPermissibleForObjectImplementation( - const SceneUtil::Optimizer* optimizer, const osg::Node* node, unsigned int option) const override - { - return (node->getDataVariance() != osg::Object::DYNAMIC); - } - }; + const unsigned char lod = static_cast(lodFlags >> (4 * 4)); + osg::ref_ptr node = createChunk(size, center, activeGrid, viewPoint, compile, lod); + mCache->addEntryToObjectCache(id, node.get()); + return node; + } namespace { + class CanOptimizeCallback : public SceneUtil::Optimizer::IsOperationPermissibleForObjectCallback + { + public: + bool isOperationPermissibleForObjectImplementation( + const SceneUtil::Optimizer* optimizer, const osg::Drawable* node, unsigned int option) const override + { + return true; + } + bool isOperationPermissibleForObjectImplementation( + const SceneUtil::Optimizer* optimizer, const osg::Node* node, unsigned int option) const override + { + return (node->getDataVariance() != osg::Object::DYNAMIC); + } + }; + using LODRange = osg::LOD::MinMaxPair; LODRange intersection(const LODRange& left, const LODRange& right) @@ -127,326 +127,327 @@ namespace MWRender { return { r.first / div, r.second / div }; } - } - class CopyOp : public osg::CopyOp - { - public: - bool mOptimizeBillboards = true; - LODRange mDistances = { 0.f, 0.f }; - osg::Vec3f mViewVector; - osg::Node::NodeMask mCopyMask = ~0u; - mutable std::vector mNodePath; - - void copy(const osg::Node* toCopy, osg::Group* attachTo) + class CopyOp : public osg::CopyOp { - const osg::Group* groupToCopy = toCopy->asGroup(); - if (toCopy->getStateSet() || toCopy->asTransform() || !groupToCopy) - attachTo->addChild(operator()(toCopy)); - else + public: + bool mOptimizeBillboards = true; + LODRange mDistances = { 0.f, 0.f }; + osg::Vec3f mViewVector; + osg::Node::NodeMask mCopyMask = ~0u; + mutable std::vector mNodePath; + + void copy(const osg::Node* toCopy, osg::Group* attachTo) { - for (unsigned int i = 0; i < groupToCopy->getNumChildren(); ++i) - attachTo->addChild(operator()(groupToCopy->getChild(i))); - } - } - - osg::Node* operator()(const osg::Node* node) const override - { - if (!(node->getNodeMask() & mCopyMask)) - return nullptr; - - if (const osg::Drawable* d = node->asDrawable()) - return operator()(d); - - if (dynamic_cast(node)) - return nullptr; - if (dynamic_cast(node)) - return nullptr; - - if (const osg::Switch* sw = node->asSwitch()) - { - osg::Group* n = new osg::Group; - for (unsigned int i = 0; i < sw->getNumChildren(); ++i) - if (sw->getValue(i)) - n->addChild(operator()(sw->getChild(i))); - n->setDataVariance(osg::Object::STATIC); - return n; - } - if (const osg::LOD* lod = dynamic_cast(node)) - { - std::vector, LODRange>> children; - for (unsigned int i = 0; i < lod->getNumChildren(); ++i) - 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) - return children.front().first.release(); + const osg::Group* groupToCopy = toCopy->asGroup(); + if (toCopy->getStateSet() || toCopy->asTransform() || !groupToCopy) + attachTo->addChild(operator()(toCopy)); else { - osg::LOD* n = new osg::LOD; - for (const auto& [child, range] : children) - n->addChild(child, range.first, range.second); + for (unsigned int i = 0; i < groupToCopy->getNumChildren(); ++i) + attachTo->addChild(operator()(groupToCopy->getChild(i))); + } + } + + osg::Node* operator()(const osg::Node* node) const override + { + if (!(node->getNodeMask() & mCopyMask)) + return nullptr; + + if (const osg::Drawable* d = node->asDrawable()) + return operator()(d); + + if (dynamic_cast(node)) + return nullptr; + if (dynamic_cast(node)) + return nullptr; + + if (const osg::Switch* sw = node->asSwitch()) + { + osg::Group* n = new osg::Group; + for (unsigned int i = 0; i < sw->getNumChildren(); ++i) + if (sw->getValue(i)) + n->addChild(operator()(sw->getChild(i))); n->setDataVariance(osg::Object::STATIC); return n; } - } - if (const osg::Sequence* sq = dynamic_cast(node)) - { - osg::Group* n = new osg::Group; - n->addChild(operator()(sq->getChild(sq->getValue() != -1 ? sq->getValue() : 0))); - n->setDataVariance(osg::Object::STATIC); - return n; - } - - mNodePath.push_back(node); - - osg::Node* cloned = static_cast(node->clone(*this)); - cloned->setDataVariance(osg::Object::STATIC); - cloned->setUserDataContainer(nullptr); - cloned->setName(""); - - mNodePath.pop_back(); - - handleCallbacks(node, cloned); - - return cloned; - } - void handleCallbacks(const osg::Node* node, osg::Node* cloned) const - { - for (const osg::Callback* callback = node->getCullCallback(); callback != nullptr; - callback = callback->getNestedCallback()) - { - if (callback->className() == std::string("BillboardCallback")) + if (const osg::LOD* lod = dynamic_cast(node)) { - if (mOptimizeBillboards) + std::vector, LODRange>> children; + for (unsigned int i = 0; i < lod->getNumChildren(); ++i) + 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) + return children.front().first.release(); + else { - handleBillboard(cloned); - continue; + 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)) + { + osg::Group* n = new osg::Group; + n->addChild(operator()(sq->getChild(sq->getValue() != -1 ? sq->getValue() : 0))); + n->setDataVariance(osg::Object::STATIC); + return n; + } + + mNodePath.push_back(node); + + osg::Node* cloned = static_cast(node->clone(*this)); + cloned->setDataVariance(osg::Object::STATIC); + cloned->setUserDataContainer(nullptr); + cloned->setName(""); + + mNodePath.pop_back(); + + handleCallbacks(node, cloned); + + return cloned; + } + void handleCallbacks(const osg::Node* node, osg::Node* cloned) const + { + for (const osg::Callback* callback = node->getCullCallback(); callback != nullptr; + callback = callback->getNestedCallback()) + { + if (callback->className() == std::string("BillboardCallback")) + { + if (mOptimizeBillboards) + { + handleBillboard(cloned); + continue; + } + else + cloned->setDataVariance(osg::Object::DYNAMIC); + } + + if (node->getCullCallback()->getNestedCallback()) + { + osg::Callback* clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY); + clonedCallback->setNestedCallback(nullptr); + cloned->addCullCallback(clonedCallback); } else - cloned->setDataVariance(osg::Object::DYNAMIC); + cloned->addCullCallback(const_cast(callback)); } + } + void handleBillboard(osg::Node* node) const + { + osg::Transform* transform = node->asTransform(); + if (!transform) + return; + osg::MatrixTransform* matrixTransform = transform->asMatrixTransform(); + if (!matrixTransform) + return; - if (node->getCullCallback()->getNestedCallback()) + osg::Matrix worldToLocal = osg::Matrix::identity(); + for (auto pathNode : mNodePath) + if (const osg::Transform* t = pathNode->asTransform()) + t->computeWorldToLocalMatrix(worldToLocal, nullptr); + worldToLocal = osg::Matrix::orthoNormal(worldToLocal); + + osg::Matrix billboardMatrix; + osg::Vec3f viewVector = -(mViewVector + worldToLocal.getTrans()); + viewVector.normalize(); + osg::Vec3f right = viewVector ^ osg::Vec3f(0, 0, 1); + right.normalize(); + osg::Vec3f up = right ^ viewVector; + up.normalize(); + billboardMatrix.makeLookAt(osg::Vec3f(0, 0, 0), viewVector, up); + billboardMatrix.invert(billboardMatrix); + + const osg::Matrix& oldMatrix = matrixTransform->getMatrix(); + float mag[3]; // attempt to preserve scale + for (int i = 0; i < 3; ++i) + mag[i] = std::sqrt(oldMatrix(0, i) * oldMatrix(0, i) + oldMatrix(1, i) * oldMatrix(1, i) + + oldMatrix(2, i) * oldMatrix(2, i)); + osg::Matrix newMatrix; + worldToLocal.setTrans(0, 0, 0); + newMatrix *= worldToLocal; + newMatrix.preMult(billboardMatrix); + newMatrix.preMultScale(osg::Vec3f(mag[0], mag[1], mag[2])); + newMatrix.setTrans(oldMatrix.getTrans()); + + matrixTransform->setMatrix(newMatrix); + } + osg::Drawable* operator()(const osg::Drawable* drawable) const override + { + if (!(drawable->getNodeMask() & mCopyMask)) + return nullptr; + + if (dynamic_cast(drawable)) + return nullptr; + + if (dynamic_cast(drawable)) + return nullptr; + if (const SceneUtil::RigGeometry* rig = dynamic_cast(drawable)) + return operator()(rig->getSourceGeometry()); + if (const SceneUtil::MorphGeometry* morph = dynamic_cast(drawable)) + return operator()(morph->getSourceGeometry()); + + if (getCopyFlags() & DEEP_COPY_DRAWABLES) { - osg::Callback* clonedCallback = osg::clone(callback, osg::CopyOp::SHALLOW_COPY); - clonedCallback->setNestedCallback(nullptr); - cloned->addCullCallback(clonedCallback); + osg::Drawable* d = static_cast(drawable->clone(*this)); + d->setDataVariance(osg::Object::STATIC); + d->setUserDataContainer(nullptr); + d->setName(""); + return d; } else - cloned->addCullCallback(const_cast(callback)); + return const_cast(drawable); } - } - void handleBillboard(osg::Node* node) const - { - osg::Transform* transform = node->asTransform(); - if (!transform) - return; - osg::MatrixTransform* matrixTransform = transform->asMatrixTransform(); - if (!matrixTransform) - return; - - osg::Matrix worldToLocal = osg::Matrix::identity(); - for (auto pathNode : mNodePath) - if (const osg::Transform* t = pathNode->asTransform()) - t->computeWorldToLocalMatrix(worldToLocal, nullptr); - worldToLocal = osg::Matrix::orthoNormal(worldToLocal); - - osg::Matrix billboardMatrix; - osg::Vec3f viewVector = -(mViewVector + worldToLocal.getTrans()); - viewVector.normalize(); - osg::Vec3f right = viewVector ^ osg::Vec3f(0, 0, 1); - right.normalize(); - osg::Vec3f up = right ^ viewVector; - up.normalize(); - billboardMatrix.makeLookAt(osg::Vec3f(0, 0, 0), viewVector, up); - billboardMatrix.invert(billboardMatrix); - - const osg::Matrix& oldMatrix = matrixTransform->getMatrix(); - float mag[3]; // attempt to preserve scale - for (int i = 0; i < 3; ++i) - mag[i] = std::sqrt(oldMatrix(0, i) * oldMatrix(0, i) + oldMatrix(1, i) * oldMatrix(1, i) - + oldMatrix(2, i) * oldMatrix(2, i)); - osg::Matrix newMatrix; - worldToLocal.setTrans(0, 0, 0); - newMatrix *= worldToLocal; - newMatrix.preMult(billboardMatrix); - newMatrix.preMultScale(osg::Vec3f(mag[0], mag[1], mag[2])); - newMatrix.setTrans(oldMatrix.getTrans()); - - matrixTransform->setMatrix(newMatrix); - } - osg::Drawable* operator()(const osg::Drawable* drawable) const override - { - if (!(drawable->getNodeMask() & mCopyMask)) - return nullptr; - - if (dynamic_cast(drawable)) - return nullptr; - - if (dynamic_cast(drawable)) - return nullptr; - if (const SceneUtil::RigGeometry* rig = dynamic_cast(drawable)) - return operator()(rig->getSourceGeometry()); - if (const SceneUtil::MorphGeometry* morph = dynamic_cast(drawable)) - return operator()(morph->getSourceGeometry()); - - if (getCopyFlags() & DEEP_COPY_DRAWABLES) - { - osg::Drawable* d = static_cast(drawable->clone(*this)); - d->setDataVariance(osg::Object::STATIC); - d->setUserDataContainer(nullptr); - d->setName(""); - return d; - } - else - return const_cast(drawable); - } - osg::Callback* operator()(const osg::Callback* callback) const override { return nullptr; } - }; - - class RefnumSet : public osg::Object - { - public: - RefnumSet() {} - RefnumSet(const RefnumSet& copy, const osg::CopyOp&) - : mRefnums(copy.mRefnums) - { - } - META_Object(MWRender, RefnumSet) - std::vector mRefnums; - }; - - class AnalyzeVisitor : public osg::NodeVisitor - { - public: - AnalyzeVisitor(osg::Node::NodeMask analyzeMask) - : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) - , mCurrentStateSet(nullptr) - { - setTraversalMask(analyzeMask); - } - - typedef std::unordered_map StateSetCounter; - struct Result - { - StateSetCounter mStateSetCounter; - unsigned int mNumVerts = 0; + osg::Callback* operator()(const osg::Callback* callback) const override { return nullptr; } }; - void apply(osg::Node& node) override + class RefnumSet : public osg::Object { - if (node.getStateSet()) - mCurrentStateSet = node.getStateSet(); + public: + RefnumSet() {} + RefnumSet(const RefnumSet& copy, const osg::CopyOp&) + : mRefnums(copy.mRefnums) + { + } + META_Object(MWRender, RefnumSet) + std::vector mRefnums; + }; - if (osg::Switch* sw = node.asSwitch()) + class AnalyzeVisitor : public osg::NodeVisitor + { + public: + AnalyzeVisitor(osg::Node::NodeMask analyzeMask) + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mCurrentStateSet(nullptr) { - for (unsigned int i = 0; i < sw->getNumChildren(); ++i) - if (sw->getValue(i)) - traverse(*sw->getChild(i)); - return; - } - if (osg::LOD* lod = dynamic_cast(&node)) - { - for (unsigned int i = 0; i < lod->getNumChildren(); ++i) - if (const auto r = intersection(lod->getRangeList()[i], mDistances); !empty(r)) - traverse(*lod->getChild(i)); - return; - } - if (osg::Sequence* sq = dynamic_cast(&node)) - { - traverse(*sq->getChild(sq->getValue() != -1 ? sq->getValue() : 0)); - return; + setTraversalMask(analyzeMask); } - traverse(node); - } - void apply(osg::Geometry& geom) override - { - if (osg::Array* array = geom.getVertexArray()) - mResult.mNumVerts += array->getNumElements(); - - ++mResult.mStateSetCounter[mCurrentStateSet]; - ++mGlobalStateSetCounter[mCurrentStateSet]; - } - Result retrieveResult() - { - Result result = mResult; - mResult = Result(); - mCurrentStateSet = nullptr; - return result; - } - void addInstance(const Result& result) - { - for (auto pair : result.mStateSetCounter) - mGlobalStateSetCounter[pair.first] += pair.second; - } - float getMergeBenefit(const Result& result) - { - if (result.mStateSetCounter.empty()) - return 1; - float mergeBenefit = 0; - for (auto pair : result.mStateSetCounter) + typedef std::unordered_map StateSetCounter; + struct Result { - mergeBenefit += mGlobalStateSetCounter[pair.first]; + StateSetCounter mStateSetCounter; + unsigned int mNumVerts = 0; + }; + + void apply(osg::Node& node) override + { + if (node.getStateSet()) + mCurrentStateSet = node.getStateSet(); + + if (osg::Switch* sw = node.asSwitch()) + { + for (unsigned int i = 0; i < sw->getNumChildren(); ++i) + if (sw->getValue(i)) + traverse(*sw->getChild(i)); + return; + } + if (osg::LOD* lod = dynamic_cast(&node)) + { + for (unsigned int i = 0; i < lod->getNumChildren(); ++i) + if (const auto r = intersection(lod->getRangeList()[i], mDistances); !empty(r)) + traverse(*lod->getChild(i)); + return; + } + if (osg::Sequence* sq = dynamic_cast(&node)) + { + traverse(*sq->getChild(sq->getValue() != -1 ? sq->getValue() : 0)); + return; + } + + traverse(node); } - mergeBenefit /= result.mStateSetCounter.size(); - return mergeBenefit; - } + void apply(osg::Geometry& geom) override + { + if (osg::Array* array = geom.getVertexArray()) + mResult.mNumVerts += array->getNumElements(); - Result mResult; - osg::StateSet* mCurrentStateSet; - StateSetCounter mGlobalStateSetCounter; - LODRange mDistances = { 0.f, 0.f }; - }; + ++mResult.mStateSetCounter[mCurrentStateSet]; + ++mGlobalStateSetCounter[mCurrentStateSet]; + } + Result retrieveResult() + { + Result result = mResult; + mResult = Result(); + mCurrentStateSet = nullptr; + return result; + } + void addInstance(const Result& result) + { + for (auto pair : result.mStateSetCounter) + mGlobalStateSetCounter[pair.first] += pair.second; + } + float getMergeBenefit(const Result& result) + { + if (result.mStateSetCounter.empty()) + return 1; + float mergeBenefit = 0; + for (auto pair : result.mStateSetCounter) + { + mergeBenefit += mGlobalStateSetCounter[pair.first]; + } + mergeBenefit /= result.mStateSetCounter.size(); + return mergeBenefit; + } - class DebugVisitor : public osg::NodeVisitor - { - public: - DebugVisitor() - : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) - { - } - void apply(osg::Drawable& node) override - { - osg::ref_ptr m(new osg::Material); - osg::Vec4f color( - Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), 0.f); - color.normalize(); - m->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f, 0.1f, 0.1f, 1.f)); - m->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f, 0.1f, 0.1f, 1.f)); - m->setColorMode(osg::Material::OFF); - m->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(color)); - osg::ref_ptr stateset - = node.getStateSet() ? osg::clone(node.getStateSet(), osg::CopyOp::SHALLOW_COPY) : new osg::StateSet; - stateset->setAttribute(m); - stateset->addUniform(new osg::Uniform("colorMode", 0)); - stateset->addUniform(new osg::Uniform("emissiveMult", 1.f)); - stateset->addUniform(new osg::Uniform("specStrength", 1.f)); - node.setStateSet(stateset); - } - }; + Result mResult; + osg::StateSet* mCurrentStateSet; + StateSetCounter mGlobalStateSetCounter; + LODRange mDistances = { 0.f, 0.f }; + }; - class AddRefnumMarkerVisitor : public osg::NodeVisitor - { - public: - AddRefnumMarkerVisitor(ESM::RefNum refnum) - : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) - , mRefnum(refnum) + class DebugVisitor : public osg::NodeVisitor { - } - ESM::RefNum mRefnum; - void apply(osg::Geometry& node) override + public: + DebugVisitor() + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + { + } + void apply(osg::Drawable& node) override + { + osg::ref_ptr m(new osg::Material); + osg::Vec4f color( + Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), Misc::Rng::rollProbability(), 0.f); + color.normalize(); + m->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f, 0.1f, 0.1f, 1.f)); + m->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(0.1f, 0.1f, 0.1f, 1.f)); + m->setColorMode(osg::Material::OFF); + m->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(color)); + osg::ref_ptr stateset = node.getStateSet() + ? osg::clone(node.getStateSet(), osg::CopyOp::SHALLOW_COPY) + : new osg::StateSet; + stateset->setAttribute(m); + stateset->addUniform(new osg::Uniform("colorMode", 0)); + stateset->addUniform(new osg::Uniform("emissiveMult", 1.f)); + stateset->addUniform(new osg::Uniform("specStrength", 1.f)); + node.setStateSet(stateset); + } + }; + + class AddRefnumMarkerVisitor : public osg::NodeVisitor { - osg::ref_ptr marker(new RefnumMarker); - marker->mRefnum = mRefnum; - if (osg::Array* array = node.getVertexArray()) - marker->mNumVertices = array->getNumElements(); - node.getOrCreateUserDataContainer()->addUserObject(marker); - } - }; + public: + AddRefnumMarkerVisitor(ESM::RefNum refnum) + : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) + , mRefnum(refnum) + { + } + ESM::RefNum mRefnum; + void apply(osg::Geometry& node) override + { + osg::ref_ptr marker(new RefnumMarker); + marker->mRefnum = mRefnum; + if (osg::Array* array = node.getVertexArray()) + marker->mNumVertices = array->getNumElements(); + node.getOrCreateUserDataContainer()->addUserObject(marker); + } + }; + } ObjectPaging::ObjectPaging(Resource::SceneManager* sceneManager, ESM::RefId worldspace) : GenericResourceManager(nullptr, Settings::cells().mCacheExpiryDelay) @@ -462,90 +463,109 @@ namespace MWRender { } - std::map ObjectPaging::collectESM3References( - float size, const osg::Vec2i& startCell, ESM::ReadersCache& readers) const + namespace { - std::map refs; - const auto& store = MWBase::Environment::get().getWorld()->getStore(); - for (int cellX = startCell.x(); cellX < startCell.x() + size; ++cellX) + struct PagedCellRef { - for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY) + ESM::RefId mRefId; + ESM::RefNum mRefNum; + osg::Vec3f mPosition; + osg::Vec3f mRotation; + float mScale; + }; + + PagedCellRef makePagedCellRef(const ESM::CellRef& value) + { + return PagedCellRef{ + .mRefId = value.mRefID, + .mRefNum = value.mRefNum, + .mPosition = value.mPos.asVec3(), + .mRotation = value.mPos.asRotationVec3(), + .mScale = value.mScale, + }; + } + + std::map collectESM3References( + float size, const osg::Vec2i& startCell, const MWWorld::ESMStore& store) + { + std::map refs; + ESM::ReadersCache readers; + for (int cellX = startCell.x(); cellX < startCell.x() + size; ++cellX) { - const ESM::Cell* cell = store.get().searchStatic(cellX, cellY); - if (!cell) - continue; - for (size_t i = 0; i < cell->mContextList.size(); ++i) + for (int cellY = startCell.y(); cellY < startCell.y() + size; ++cellY) { - try + const ESM::Cell* cell = store.get().searchStatic(cellX, cellY); + if (!cell) + continue; + for (size_t i = 0; i < cell->mContextList.size(); ++i) { - const std::size_t index = static_cast(cell->mContextList[i].index); - const ESM::ReadersCache::BusyItem reader = readers.get(index); - cell->restore(*reader, i); - ESM::CellRef ref; - ESM::MovedCellRef cMRef; - bool deleted = false; - bool moved = false; - while (ESM::Cell::getNextRef( - *reader, ref, deleted, cMRef, moved, ESM::Cell::GetNextRefMode::LoadOnlyNotMoved)) + try { - if (moved) - continue; - - if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) - != cell->mMovedRefs.end()) - continue; - - int type = store.findStatic(ref.mRefID); - if (!typeFilter(type, size >= 2)) - continue; - if (deleted) + const std::size_t index = static_cast(cell->mContextList[i].index); + const ESM::ReadersCache::BusyItem reader = readers.get(index); + cell->restore(*reader, i); + ESM::CellRef ref; + ESM::MovedCellRef cMRef; + bool deleted = false; + bool moved = false; + while (ESM::Cell::getNextRef( + *reader, ref, deleted, cMRef, moved, ESM::Cell::GetNextRefMode::LoadOnlyNotMoved)) { - refs.erase(ref.mRefNum); - continue; + if (moved) + continue; + + if (std::find(cell->mMovedRefs.begin(), cell->mMovedRefs.end(), ref.mRefNum) + != cell->mMovedRefs.end()) + continue; + + int type = store.findStatic(ref.mRefID); + if (!typeFilter(type, size >= 2)) + continue; + if (deleted) + { + refs.erase(ref.mRefNum); + continue; + } + refs.insert_or_assign(ref.mRefNum, makePagedCellRef(ref)); } - refs[ref.mRefNum] = std::move(ref); + } + catch (const std::exception& e) + { + Log(Debug::Warning) << "Failed to collect references from cell \"" << cell->getDescription() + << "\": " << e.what(); + continue; } } - catch (const std::exception& e) + for (const auto& [ref, deleted] : cell->mLeasedRefs) { - Log(Debug::Warning) << "Failed to collect references from cell \"" << cell->getDescription() - << "\": " << e.what(); - continue; + if (deleted) + { + refs.erase(ref.mRefNum); + continue; + } + int type = store.findStatic(ref.mRefID); + if (!typeFilter(type, size >= 2)) + continue; + refs.insert_or_assign(ref.mRefNum, makePagedCellRef(ref)); } } - for (auto [ref, deleted] : cell->mLeasedRefs) - { - if (deleted) - { - refs.erase(ref.mRefNum); - continue; - } - int type = store.findStatic(ref.mRefID); - if (!typeFilter(type, size >= 2)) - continue; - refs[ref.mRefNum] = std::move(ref); - } } + return refs; } - return refs; } osg::ref_ptr ObjectPaging::createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile, unsigned char lod) { - osg::Vec2i startCell = osg::Vec2i(std::floor(center.x() - size / 2.f), std::floor(center.y() - size / 2.f)); + const osg::Vec2i startCell(std::floor(center.x() - size / 2.f), std::floor(center.y() - size / 2.f)); + const MWBase::World& world = *MWBase::Environment::get().getWorld(); + const MWWorld::ESMStore& store = world.getStore(); - osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0) * getCellSize(mWorldspace); - osg::Vec3f relativeViewPoint = viewPoint - worldCenter; - - std::map refs; - ESM::ReadersCache readers; - const auto& world = MWBase::Environment::get().getWorld(); - const auto& store = world->getStore(); + std::map refs; if (mWorldspace == ESM::Cell::sDefaultWorldspaceId) { - refs = collectESM3References(size, startCell, readers); + refs = collectESM3References(size, startCell, store); } else { @@ -567,17 +587,19 @@ namespace MWRender } } - osg::Vec2f minBound = (center - osg::Vec2f(size / 2.f, size / 2.f)); - osg::Vec2f maxBound = (center + osg::Vec2f(size / 2.f, size / 2.f)); + const osg::Vec2f minBound = (center - osg::Vec2f(size / 2.f, size / 2.f)); + const osg::Vec2f maxBound = (center + osg::Vec2f(size / 2.f, size / 2.f)); + const osg::Vec2i floorMinBound(std::floor(minBound.x()), std::floor(minBound.y())); + const osg::Vec2i ceilMaxBound(std::ceil(maxBound.x()), std::ceil(maxBound.y())); struct InstanceList { - std::vector mInstances; + std::vector mInstances; AnalyzeVisitor::Result mAnalyzeResult; bool mNeedCompile = false; }; typedef std::map, InstanceList> NodeMap; NodeMap nodes; - osg::ref_ptr refnumSet = activeGrid ? new RefnumSet : nullptr; + const osg::ref_ptr refnumSet = activeGrid ? new RefnumSet : nullptr; // Mask_UpdateVisitor is used in such cases in NIF loader: // 1. For collision nodes, which is not supposed to be rendered. @@ -585,47 +607,39 @@ namespace MWRender // Since ObjectPaging does not handle VisController, we can just ignore both types of nodes. constexpr auto copyMask = ~Mask_UpdateVisitor; - auto cellSize = getCellSize(mWorldspace); - const auto smallestDistanceToChunk = (size > 1 / 8.f) ? (size * cellSize) : 0.f; - const auto higherDistanceToChunk = [&] { - if (!activeGrid) - return smallestDistanceToChunk + 1; - return ((size < 1) ? 5 : 3) * cellSize * size + 1; - }(); + const int cellSize = getCellSize(mWorldspace); + const float smallestDistanceToChunk = (size > 1 / 8.f) ? (size * cellSize) : 0.f; + const float higherDistanceToChunk + = activeGrid ? ((size < 1) ? 5 : 3) * cellSize * size + 1 : smallestDistanceToChunk + 1; AnalyzeVisitor analyzeVisitor(copyMask); - float minSize = mMinSize; - if (mMinSizeMergeFactor) - minSize *= mMinSizeMergeFactor; - for (const auto& pair : refs) + const float minSize = mMinSizeMergeFactor ? mMinSize * mMinSizeMergeFactor : mMinSize; + for (const auto& [refNum, ref] : refs) { - const ESM::CellRef& ref = pair.second; - - osg::Vec3f pos = ref.mPos.asVec3(); if (size < 1.f) { - osg::Vec3f cellPos = pos / cellSize; - if ((minBound.x() > std::floor(minBound.x()) && cellPos.x() < minBound.x()) - || (minBound.y() > std::floor(minBound.y()) && cellPos.y() < minBound.y()) - || (maxBound.x() < std::ceil(maxBound.x()) && cellPos.x() >= maxBound.x()) - || (maxBound.y() < std::ceil(maxBound.y()) && cellPos.y() >= maxBound.y())) + const osg::Vec3f cellPos = ref.mPosition / cellSize; + if ((minBound.x() > floorMinBound.x() && cellPos.x() < minBound.x()) + || (minBound.y() > floorMinBound.y() && cellPos.y() < minBound.y()) + || (maxBound.x() < ceilMaxBound.x() && cellPos.x() >= maxBound.x()) + || (maxBound.y() < ceilMaxBound.y() && cellPos.y() >= maxBound.y())) continue; } - float dSqr = (viewPoint - pos).length2(); + const float dSqr = (viewPoint - ref.mPosition).length2(); if (!activeGrid) { std::lock_guard lock(mSizeCacheMutex); - SizeCache::iterator found = mSizeCache.find(pair.first); + SizeCache::iterator found = mSizeCache.find(refNum); if (found != mSizeCache.end() && found->second < dSqr * minSize * minSize) continue; } - if (Misc::ResourceHelpers::isHiddenMarker(ref.mRefID)) + if (Misc::ResourceHelpers::isHiddenMarker(ref.mRefId)) continue; - int type = store.findStatic(ref.mRefID); - std::string model = getModel(type, ref.mRefID, store); + const int type = store.findStatic(ref.mRefId); + std::string model = getModel(type, ref.mRefId, store); if (model.empty()) continue; model = Misc::ResourceHelpers::correctMeshPath(model); @@ -651,11 +665,9 @@ namespace MWRender model = found->second; else model = mLODNameCache - .insert(found, - { key, - Misc::ResourceHelpers::getLODMeshName( - world->getESMVersions()[ref.mRefNum.mContentFile], model, - mSceneManager->getVFS(), lod) }) + .emplace_hint(found, std::move(key), + Misc::ResourceHelpers::getLODMeshName(world.getESMVersions()[refNum.mContentFile], + model, mSceneManager->getVFS(), lod)) ->second; } @@ -670,38 +682,39 @@ namespace MWRender && dynamic_cast(cnode->getUpdateCallback()))) continue; else - refnumSet->mRefnums.push_back(pair.first); + refnumSet->mRefnums.push_back(refNum); } { std::lock_guard lock(mRefTrackerMutex); - if (getRefTracker().mDisabled.count(pair.first)) + if (getRefTracker().mDisabled.count(refNum)) continue; } - float radius2 = cnode->getBound().radius2() * ref.mScale * ref.mScale; + const float radius2 = cnode->getBound().radius2() * ref.mScale * ref.mScale; if (radius2 < dSqr * minSize * minSize && !activeGrid) { std::lock_guard lock(mSizeCacheMutex); - mSizeCache[pair.first] = radius2; + mSizeCache[refNum] = radius2; continue; } - auto emplaced = nodes.emplace(cnode, InstanceList()); + const auto emplaced = nodes.emplace(std::move(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 + const osg::Node* const nodePtr = emplaced.first->first.get(); + // const-trickery required because there is no const version of NodeVisitor + const_cast(nodePtr)->accept(analyzeVisitor); emplaced.first->second.mAnalyzeResult = analyzeVisitor.retrieveResult(); - emplaced.first->second.mNeedCompile = compile && cnode->referenceCount() <= 3; + emplaced.first->second.mNeedCompile = compile && nodePtr->referenceCount() <= 2; } else analyzeVisitor.addInstance(emplaced.first->second.mAnalyzeResult); emplaced.first->second.mInstances.push_back(&ref); } + const osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0) * getCellSize(mWorldspace); osg::ref_ptr group = new osg::Group; osg::ref_ptr mergeGroup = new osg::Group; osg::ref_ptr templateRefs = new Resource::TemplateMultiRef; @@ -714,32 +727,30 @@ namespace MWRender const AnalyzeVisitor::Result& analyzeResult = pair.second.mAnalyzeResult; - float mergeCost = analyzeResult.mNumVerts * size; - float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; - bool merge = mergeBenefit > mergeCost; + const float mergeCost = analyzeResult.mNumVerts * size; + const float mergeBenefit = analyzeVisitor.getMergeBenefit(analyzeResult) * mMergeFactor; + const bool merge = mergeBenefit > mergeCost; - float minSizeMerged = mMinSize; - float factor2 = mergeBenefit > 0 ? std::min(1.f, mergeCost * mMinSizeCostMultiplier / mergeBenefit) : 1; - float minSizeMergeFactor2 = (1 - factor2) * mMinSizeMergeFactor + factor2; - if (minSizeMergeFactor2 > 0) - minSizeMerged *= minSizeMergeFactor2; + const float factor2 + = mergeBenefit > 0 ? std::min(1.f, mergeCost * mMinSizeCostMultiplier / mergeBenefit) : 1; + const float minSizeMergeFactor2 = (1 - factor2) * mMinSizeMergeFactor + factor2; + const float minSizeMerged = minSizeMergeFactor2 > 0 ? mMinSize * minSizeMergeFactor2 : mMinSize; unsigned int numinstances = 0; - for (auto cref : pair.second.mInstances) + for (const PagedCellRef* refPtr : pair.second.mInstances) { - const ESM::CellRef& ref = *cref; - osg::Vec3f pos = ref.mPos.asVec3(); + const PagedCellRef& ref = *refPtr; if (!activeGrid && minSizeMerged != minSize - && cnode->getBound().radius2() * cref->mScale * cref->mScale - < (viewPoint - pos).length2() * minSizeMerged * minSizeMerged) + && cnode->getBound().radius2() * ref.mScale * ref.mScale + < (viewPoint - ref.mPosition).length2() * minSizeMerged * minSizeMerged) continue; - osg::Vec3f nodePos = pos - worldCenter; - osg::Quat nodeAttitude = osg::Quat(ref.mPos.rot[2], osg::Vec3f(0, 0, -1)) - * osg::Quat(ref.mPos.rot[1], osg::Vec3f(0, -1, 0)) - * osg::Quat(ref.mPos.rot[0], osg::Vec3f(-1, 0, 0)); - osg::Vec3f nodeScale = osg::Vec3f(ref.mScale, ref.mScale, ref.mScale); + const osg::Vec3f nodePos = ref.mPosition - worldCenter; + const osg::Quat nodeAttitude = osg::Quat(ref.mRotation.z(), osg::Vec3f(0, 0, -1)) + * osg::Quat(ref.mRotation.y(), osg::Vec3f(0, -1, 0)) + * osg::Quat(ref.mRotation.x(), osg::Vec3f(-1, 0, 0)); + const osg::Vec3f nodeScale(ref.mScale, ref.mScale, ref.mScale); osg::ref_ptr trans; if (merge) @@ -792,7 +803,7 @@ namespace MWRender } } - osg::Group* attachTo = merge ? mergeGroup : group; + osg::Group* const attachTo = merge ? mergeGroup : group; attachTo->addChild(trans); ++numinstances; } @@ -813,6 +824,8 @@ namespace MWRender } } + const osg::Vec3f relativeViewPoint = viewPoint - worldCenter; + if (mergeGroup->getNumChildren()) { SceneUtil::Optimizer optimizer; @@ -822,7 +835,7 @@ namespace MWRender optimizer.setMergeAlphaBlending(true); } optimizer.setIsOperationPermissibleForObjectCallback(new CanOptimizeCallback); - unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS + const unsigned int options = SceneUtil::Optimizer::FLATTEN_STATIC_TRANSFORMS | SceneUtil::Optimizer::REMOVE_REDUNDANT_NODES | SceneUtil::Optimizer::MERGE_GEOMETRY; optimizer.optimize(mergeGroup, options); @@ -841,7 +854,7 @@ namespace MWRender } } - auto ico = mSceneManager->getIncrementalCompileOperation(); + osgUtil::IncrementalCompileOperation* const ico = mSceneManager->getIncrementalCompileOperation(); if (!stateToCompile.empty() && ico) { auto compileSet = new osgUtil::IncrementalCompileOperation::CompileSet(group); @@ -984,34 +997,37 @@ namespace MWRender return true; } - struct GetRefnumsFunctor + namespace { - GetRefnumsFunctor(std::vector& output) - : mOutput(output) + struct GetRefnumsFunctor { - } - void operator()(MWRender::ChunkId chunkId, osg::Object* obj) - { - if (!std::get<2>(chunkId)) - return; - const osg::Vec2f& center = std::get<0>(chunkId); - bool activeGrid = (center.x() > mActiveGrid.x() || center.y() > mActiveGrid.y() - || center.x() < mActiveGrid.z() || center.y() < mActiveGrid.w()); - if (!activeGrid) - return; - - osg::UserDataContainer* udc = obj->getUserDataContainer(); - if (udc && udc->getNumUserObjects()) + GetRefnumsFunctor(std::vector& output) + : mOutput(output) { - RefnumSet* refnums = dynamic_cast(udc->getUserObject(0)); - if (!refnums) - return; - mOutput.insert(mOutput.end(), refnums->mRefnums.begin(), refnums->mRefnums.end()); } - } - osg::Vec4i mActiveGrid; - std::vector& mOutput; - }; + void operator()(MWRender::ChunkId chunkId, osg::Object* obj) + { + if (!std::get<2>(chunkId)) + return; + const osg::Vec2f& center = std::get<0>(chunkId); + const bool activeGrid = (center.x() > mActiveGrid.x() || center.y() > mActiveGrid.y() + || center.x() < mActiveGrid.z() || center.y() < mActiveGrid.w()); + if (!activeGrid) + return; + + osg::UserDataContainer* udc = obj->getUserDataContainer(); + if (udc && udc->getNumUserObjects()) + { + RefnumSet* refnums = dynamic_cast(udc->getUserObject(0)); + if (!refnums) + return; + mOutput.insert(mOutput.end(), refnums->mRefnums.begin(), refnums->mRefnums.end()); + } + } + osg::Vec4i mActiveGrid; + std::vector& mOutput; + }; + } void ObjectPaging::getPagedRefnums(const osg::Vec4i& activeGrid, std::vector& out) { diff --git a/apps/openmw/mwrender/objectpaging.hpp b/apps/openmw/mwrender/objectpaging.hpp index 6085887b9c..11be6009ca 100644 --- a/apps/openmw/mwrender/objectpaging.hpp +++ b/apps/openmw/mwrender/objectpaging.hpp @@ -1,7 +1,7 @@ #ifndef OPENMW_MWRENDER_OBJECTPAGING_H #define OPENMW_MWRENDER_OBJECTPAGING_H -#include +#include #include #include @@ -11,15 +11,6 @@ namespace Resource { class SceneManager; } -namespace MWWorld -{ - class ESMStore; -} - -namespace ESM -{ - class ReadersCache; -} namespace MWRender { @@ -82,9 +73,6 @@ namespace MWRender const RefTracker& getRefTracker() const { return mRefTracker; } RefTracker& getWritableRefTracker() { return mRefTrackerLocked ? mRefTrackerNew : mRefTracker; } - std::map collectESM3References( - float size, const osg::Vec2i& startCell, ESM::ReadersCache& readers) const; - std::mutex mSizeCacheMutex; typedef std::map SizeCache; SizeCache mSizeCache; diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index b7db68214d..ebc92bcdbd 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -239,6 +239,8 @@ namespace MWWorld MWWorld::ESMStore& getStore() override { return mStore; } + const MWWorld::ESMStore& getStore() const override { return mStore; } + const std::vector& getESMVersions() const override; LocalScripts& getLocalScripts() override;