Merge branch 'vfs_normalized_path_17' into 'master'

Use normalized path for groundcover (#8138)

See merge request OpenMW/openmw!4403
pull/3236/head
psi29a 3 months ago
commit 4ac1b13871

@ -1,5 +1,7 @@
#include "groundcover.hpp" #include "groundcover.hpp"
#include <span>
#include <osg/AlphaFunc> #include <osg/AlphaFunc>
#include <osg/BlendFunc> #include <osg/BlendFunc>
#include <osg/ComputeBoundsVisitor> #include <osg/ComputeBoundsVisitor>
@ -46,13 +48,13 @@ namespace MWRender
class InstancedComputeNearFarCullCallback : public osg::DrawableCullCallback class InstancedComputeNearFarCullCallback : public osg::DrawableCullCallback
{ {
public: public:
InstancedComputeNearFarCullCallback(const std::vector<Groundcover::GroundcoverEntry>& instances, explicit InstancedComputeNearFarCullCallback(std::span<const Groundcover::GroundcoverEntry> instances,
const osg::Vec3& chunkPosition, const osg::BoundingBox& instanceBounds) const osg::Vec3& chunkPosition, const osg::BoundingBox& instanceBounds)
: mInstanceMatrices() : mInstanceMatrices()
, mInstanceBounds(instanceBounds) , mInstanceBounds(instanceBounds)
{ {
mInstanceMatrices.reserve(instances.size()); mInstanceMatrices.reserve(instances.size());
for (const auto& instance : instances) for (const Groundcover::GroundcoverEntry& instance : instances)
mInstanceMatrices.emplace_back(computeInstanceMatrix(instance, chunkPosition)); mInstanceMatrices.emplace_back(computeInstanceMatrix(instance, chunkPosition));
} }
@ -191,7 +193,8 @@ namespace MWRender
class InstancingVisitor : public osg::NodeVisitor class InstancingVisitor : public osg::NodeVisitor
{ {
public: public:
InstancingVisitor(std::vector<Groundcover::GroundcoverEntry>& instances, osg::Vec3f& chunkPosition) explicit InstancingVisitor(
std::span<const Groundcover::GroundcoverEntry> instances, osg::Vec3f& chunkPosition)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mInstances(instances) , mInstances(instances)
, mChunkPosition(chunkPosition) , mChunkPosition(chunkPosition)
@ -252,7 +255,7 @@ namespace MWRender
} }
private: private:
std::vector<Groundcover::GroundcoverEntry> mInstances; std::span<const Groundcover::GroundcoverEntry> mInstances;
osg::Vec3f mChunkPosition; osg::Vec3f mChunkPosition;
}; };
@ -411,12 +414,15 @@ namespace MWRender
} }
} }
for (auto& pair : refs) for (auto& [refNum, cellRef] : refs)
{ {
ESM::CellRef& ref = pair.second; const VFS::Path::NormalizedView model = mGroundcoverStore.getGroundcoverModel(cellRef.mRefID);
const std::string& model = mGroundcoverStore.getGroundcoverModel(ref.mRefID); if (model.empty())
if (!model.empty()) continue;
instances[model].emplace_back(std::move(ref)); auto it = instances.find(model);
if (it == instances.end())
it = instances.emplace_hint(it, VFS::Path::Normalized(model), std::vector<GroundcoverEntry>());
it->second.emplace_back(std::move(cellRef));
} }
} }
} }
@ -426,9 +432,9 @@ namespace MWRender
{ {
osg::ref_ptr<osg::Group> group = new osg::Group; osg::ref_ptr<osg::Group> group = new osg::Group;
osg::Vec3f worldCenter = osg::Vec3f(center.x(), center.y(), 0) * ESM::Land::REAL_SIZE; 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<osg::Node> node = static_cast<osg::Node*>(temp->clone(osg::CopyOp::DEEP_COPY_NODES osg::ref_ptr<osg::Node> node = static_cast<osg::Node*>(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_DRAWABLES | osg::CopyOp::DEEP_COPY_USERDATA | osg::CopyOp::DEEP_COPY_ARRAYS
| osg::CopyOp::DEEP_COPY_PRIMITIVES)); | osg::CopyOp::DEEP_COPY_PRIMITIVES));
@ -436,7 +442,7 @@ namespace MWRender
// Keep link to original mesh to keep it in cache // Keep link to original mesh to keep it in cache
group->getOrCreateUserDataContainer()->addUserObject(new Resource::TemplateRef(temp)); group->getOrCreateUserDataContainer()->addUserObject(new Resource::TemplateRef(temp));
InstancingVisitor visitor(pair.second, worldCenter); InstancingVisitor visitor(entries, worldCenter);
node->accept(visitor); node->accept(visitor);
group->addChild(node); group->addChild(node);
} }

@ -4,6 +4,7 @@
#include <components/esm3/loadcell.hpp> #include <components/esm3/loadcell.hpp>
#include <components/resource/scenemanager.hpp> #include <components/resource/scenemanager.hpp>
#include <components/terrain/quadtreeworld.hpp> #include <components/terrain/quadtreeworld.hpp>
#include <components/vfs/pathutil.hpp>
namespace MWWorld namespace MWWorld
{ {
@ -46,13 +47,14 @@ namespace MWRender
}; };
private: private:
using InstanceMap = std::map<VFS::Path::Normalized, std::vector<GroundcoverEntry>, std::less<>>;
Resource::SceneManager* mSceneManager; Resource::SceneManager* mSceneManager;
float mDensity; float mDensity;
osg::ref_ptr<osg::StateSet> mStateset; osg::ref_ptr<osg::StateSet> mStateset;
osg::ref_ptr<osg::Program> mProgramTemplate; osg::ref_ptr<osg::Program> mProgramTemplate;
const MWWorld::GroundcoverStore& mGroundcoverStore; const MWWorld::GroundcoverStore& mGroundcoverStore;
typedef std::map<std::string, std::vector<GroundcoverEntry>> InstanceMap;
osg::ref_ptr<osg::Node> createChunk(InstanceMap& instances, const osg::Vec2f& center); osg::ref_ptr<osg::Node> createChunk(InstanceMap& instances, const osg::Vec2f& center);
void collectInstances(InstanceMap& instances, float size, const osg::Vec2f& center); void collectInstances(InstanceMap& instances, float size, const osg::Vec2f& center);
}; };

@ -21,29 +21,27 @@ namespace MWWorld
query.mLoadCells = true; query.mLoadCells = true;
ESM::ReadersCache readers; ESM::ReadersCache readers;
const ::EsmLoader::EsmData content ::EsmLoader::EsmData content
= ::EsmLoader::loadEsmData(query, groundcoverFiles, fileCollections, readers, encoder, listener); = ::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) for (const ESM::Static& stat : statics)
{ {
std::string model = Misc::StringUtils::lowerCase(stat.mModel); VFS::Path::Normalized model = VFS::Path::toNormalized(stat.mModel);
std::replace(model.begin(), model.end(), '/', '\\'); if (!model.value().starts_with(prefix))
if (!model.starts_with(prefix))
continue; continue;
mMeshCache[stat.mId] = Misc::ResourceHelpers::correctMeshPath(model); mMeshCache[stat.mId] = Misc::ResourceHelpers::correctMeshPath(model);
} }
for (const ESM::Static& stat : content.mStatics) for (const ESM::Static& stat : content.mStatics)
{ {
std::string model = Misc::StringUtils::lowerCase(stat.mModel); VFS::Path::Normalized model = VFS::Path::toNormalized(stat.mModel);
std::replace(model.begin(), model.end(), '/', '\\'); if (!model.value().starts_with(prefix))
if (!model.starts_with(prefix))
continue; continue;
mMeshCache[stat.mId] = Misc::ResourceHelpers::correctMeshPath(model); mMeshCache[stat.mId] = Misc::ResourceHelpers::correctMeshPath(model);
} }
for (const ESM::Cell& cell : content.mCells) for (ESM::Cell& cell : content.mCells)
{ {
if (!cell.isExterior()) if (!cell.isExterior())
continue; 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 void GroundcoverStore::initCell(ESM::Cell& cell, int cellX, int cellY) const
{ {
cell.blank(); cell.blank();

@ -2,6 +2,8 @@
#define GAME_MWWORLD_GROUNDCOVER_STORE_H #define GAME_MWWORLD_GROUNDCOVER_STORE_H
#include <components/esm/refid.hpp> #include <components/esm/refid.hpp>
#include <components/vfs/pathutil.hpp>
#include <map> #include <map>
#include <string> #include <string>
#include <vector> #include <vector>
@ -36,7 +38,7 @@ namespace MWWorld
class GroundcoverStore class GroundcoverStore
{ {
private: private:
std::map<ESM::RefId, std::string> mMeshCache; std::map<ESM::RefId, VFS::Path::Normalized> mMeshCache;
std::map<std::pair<int, int>, std::vector<ESM::ESM_Context>> mCellContexts; std::map<std::pair<int, int>, std::vector<ESM::ESM_Context>> mCellContexts;
public: public:
@ -44,7 +46,14 @@ namespace MWWorld
const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder, const std::vector<std::string>& groundcoverFiles, ToUTF8::Utf8Encoder* encoder,
Loading::Listener* listener); 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; void initCell(ESM::Cell& cell, int cellX, int cellY) const;
}; };
} }

@ -94,6 +94,8 @@ namespace VFS::Path
constexpr std::string_view value() const noexcept { return mValue; } 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 NormalizedView& rhs) = default;
friend constexpr bool operator==(const NormalizedView& lhs, const auto& rhs) { return lhs.mValue == rhs; } friend constexpr bool operator==(const NormalizedView& lhs, const auto& rhs) { return lhs.mValue == rhs; }

Loading…
Cancel
Save