diff --git a/apps/openmw/mwrender/groundcover.cpp b/apps/openmw/mwrender/groundcover.cpp index 44608ca3c3..9fc40f3e19 100644 --- a/apps/openmw/mwrender/groundcover.cpp +++ b/apps/openmw/mwrender/groundcover.cpp @@ -1,5 +1,7 @@ #include "groundcover.hpp" +#include + #include #include #include @@ -46,13 +48,13 @@ namespace MWRender class InstancedComputeNearFarCullCallback : public osg::DrawableCullCallback { public: - InstancedComputeNearFarCullCallback(const std::vector& instances, + explicit InstancedComputeNearFarCullCallback(std::span instances, const osg::Vec3& chunkPosition, const osg::BoundingBox& instanceBounds) : mInstanceMatrices() , mInstanceBounds(instanceBounds) { mInstanceMatrices.reserve(instances.size()); - for (const auto& instance : instances) + for (const Groundcover::GroundcoverEntry& instance : instances) mInstanceMatrices.emplace_back(computeInstanceMatrix(instance, chunkPosition)); } @@ -191,7 +193,8 @@ namespace MWRender class InstancingVisitor : public osg::NodeVisitor { public: - InstancingVisitor(std::vector& instances, osg::Vec3f& chunkPosition) + explicit InstancingVisitor( + std::span instances, osg::Vec3f& chunkPosition) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) , mInstances(instances) , mChunkPosition(chunkPosition) @@ -252,7 +255,7 @@ namespace MWRender } private: - std::vector mInstances; + std::span mInstances; osg::Vec3f mChunkPosition; }; @@ -411,12 +414,15 @@ namespace MWRender } } - for (auto& pair : refs) + for (auto& [refNum, cellRef] : refs) { - ESM::CellRef& ref = pair.second; - const std::string& model = mGroundcoverStore.getGroundcoverModel(ref.mRefID); - if (!model.empty()) - instances[model].emplace_back(std::move(ref)); + const VFS::Path::NormalizedView model = mGroundcoverStore.getGroundcoverModel(cellRef.mRefID); + if (model.empty()) + continue; + auto it = instances.find(model); + if (it == instances.end()) + it = instances.emplace_hint(it, VFS::Path::Normalized(model), std::vector()); + it->second.emplace_back(std::move(cellRef)); } } } @@ -426,9 +432,9 @@ namespace MWRender { osg::ref_ptr group = new osg::Group; osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0) * ESM::Land::REAL_SIZE; - for (auto& pair : instances) + for (const auto& [model, entries] : instances) { - const osg::Node* temp = mSceneManager->getTemplate(VFS::Path::toNormalized(pair.first)); + const osg::Node* temp = mSceneManager->getTemplate(model); osg::ref_ptr node = static_cast(temp->clone(osg::CopyOp::DEEP_COPY_NODES | osg::CopyOp::DEEP_COPY_DRAWABLES | osg::CopyOp::DEEP_COPY_USERDATA | osg::CopyOp::DEEP_COPY_ARRAYS | osg::CopyOp::DEEP_COPY_PRIMITIVES)); @@ -436,7 +442,7 @@ namespace MWRender // Keep link to original mesh to keep it in cache group->getOrCreateUserDataContainer()->addUserObject(new Resource::TemplateRef(temp)); - InstancingVisitor visitor(pair.second, worldCenter); + InstancingVisitor visitor(entries, worldCenter); node->accept(visitor); group->addChild(node); } diff --git a/apps/openmw/mwrender/groundcover.hpp b/apps/openmw/mwrender/groundcover.hpp index df40d9d529..ecbc990da0 100644 --- a/apps/openmw/mwrender/groundcover.hpp +++ b/apps/openmw/mwrender/groundcover.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace MWWorld { @@ -46,13 +47,14 @@ namespace MWRender }; private: + using InstanceMap = std::map, std::less<>>; + Resource::SceneManager* mSceneManager; float mDensity; osg::ref_ptr mStateset; osg::ref_ptr mProgramTemplate; const MWWorld::GroundcoverStore& mGroundcoverStore; - typedef std::map> InstanceMap; osg::ref_ptr createChunk(InstanceMap& instances, const osg::Vec2f& center); void collectInstances(InstanceMap& instances, float size, const osg::Vec2f& center); }; diff --git a/apps/openmw/mwworld/groundcoverstore.cpp b/apps/openmw/mwworld/groundcoverstore.cpp index f45b49babe..ea5b5baf5e 100644 --- a/apps/openmw/mwworld/groundcoverstore.cpp +++ b/apps/openmw/mwworld/groundcoverstore.cpp @@ -21,29 +21,27 @@ namespace MWWorld query.mLoadCells = true; ESM::ReadersCache readers; - const ::EsmLoader::EsmData content + ::EsmLoader::EsmData content = ::EsmLoader::loadEsmData(query, groundcoverFiles, fileCollections, readers, encoder, listener); - static constexpr std::string_view prefix = "grass\\"; + static constexpr std::string_view prefix = "grass/"; for (const ESM::Static& stat : statics) { - std::string model = Misc::StringUtils::lowerCase(stat.mModel); - std::replace(model.begin(), model.end(), '/', '\\'); - if (!model.starts_with(prefix)) + VFS::Path::Normalized model = VFS::Path::toNormalized(stat.mModel); + if (!model.value().starts_with(prefix)) continue; mMeshCache[stat.mId] = Misc::ResourceHelpers::correctMeshPath(model); } for (const ESM::Static& stat : content.mStatics) { - std::string model = Misc::StringUtils::lowerCase(stat.mModel); - std::replace(model.begin(), model.end(), '/', '\\'); - if (!model.starts_with(prefix)) + VFS::Path::Normalized model = VFS::Path::toNormalized(stat.mModel); + if (!model.value().starts_with(prefix)) continue; mMeshCache[stat.mId] = Misc::ResourceHelpers::correctMeshPath(model); } - for (const ESM::Cell& cell : content.mCells) + for (ESM::Cell& cell : content.mCells) { if (!cell.isExterior()) continue; @@ -52,15 +50,6 @@ namespace MWWorld } } - std::string GroundcoverStore::getGroundcoverModel(const ESM::RefId& id) const - { - auto search = mMeshCache.find(id); - if (search == mMeshCache.end()) - return std::string(); - - return search->second; - } - void GroundcoverStore::initCell(ESM::Cell& cell, int cellX, int cellY) const { cell.blank(); diff --git a/apps/openmw/mwworld/groundcoverstore.hpp b/apps/openmw/mwworld/groundcoverstore.hpp index 2f34aa9675..021bdb5e91 100644 --- a/apps/openmw/mwworld/groundcoverstore.hpp +++ b/apps/openmw/mwworld/groundcoverstore.hpp @@ -2,6 +2,8 @@ #define GAME_MWWORLD_GROUNDCOVER_STORE_H #include +#include + #include #include #include @@ -36,7 +38,7 @@ namespace MWWorld class GroundcoverStore { private: - std::map mMeshCache; + std::map mMeshCache; std::map, std::vector> mCellContexts; public: @@ -44,7 +46,14 @@ namespace MWWorld const std::vector& groundcoverFiles, ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener); - std::string getGroundcoverModel(const ESM::RefId& id) const; + VFS::Path::NormalizedView getGroundcoverModel(ESM::RefId id) const + { + auto it = mMeshCache.find(id); + if (it == mMeshCache.end()) + return {}; + return it->second; + } + void initCell(ESM::Cell& cell, int cellX, int cellY) const; }; } diff --git a/components/vfs/pathutil.hpp b/components/vfs/pathutil.hpp index 72c475fae0..4492be3967 100644 --- a/components/vfs/pathutil.hpp +++ b/components/vfs/pathutil.hpp @@ -94,6 +94,8 @@ namespace VFS::Path constexpr std::string_view value() const noexcept { return mValue; } + constexpr bool empty() const noexcept { return mValue.empty(); } + friend constexpr bool operator==(const NormalizedView& lhs, const NormalizedView& rhs) = default; friend constexpr bool operator==(const NormalizedView& lhs, const auto& rhs) { return lhs.mValue == rhs; }