mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-14 10:21:09 +00:00
Merge branch 'DistantLOD' into 'master'
Support for TES distant LOD See merge request OpenMW/openmw!1861
This commit is contained in:
commit
647b22e175
@ -14,6 +14,7 @@
|
||||
Bug #6964: Nerasa Dralor Won't Follow
|
||||
Bug #6974: Only harmful effects are reflected
|
||||
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
|
||||
|
||||
0.48.0
|
||||
------
|
||||
|
@ -160,6 +160,8 @@ namespace MWBase
|
||||
|
||||
virtual const MWWorld::ESMStore& getStore() const = 0;
|
||||
|
||||
virtual const std::vector<int>& getESMVersions() const = 0;
|
||||
|
||||
virtual MWWorld::LocalScripts& getLocalScripts() = 0;
|
||||
|
||||
virtual bool hasCellChanged() const = 0;
|
||||
|
@ -76,6 +76,7 @@ namespace MWRender
|
||||
|
||||
osg::ref_ptr<osg::Node> 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<unsigned char>(lodFlags >> (4 * 4));
|
||||
if (activeGrid && !mActiveGrid)
|
||||
return nullptr;
|
||||
|
||||
@ -86,7 +87,7 @@ namespace MWRender
|
||||
return static_cast<osg::Node*>(obj.get());
|
||||
else
|
||||
{
|
||||
osg::ref_ptr<osg::Node> node = createChunk(size, center, activeGrid, viewPoint, compile);
|
||||
osg::ref_ptr<osg::Node> node = createChunk(size, center, activeGrid, viewPoint, compile, lod);
|
||||
mCache->addEntryToObjectCache(id, node.get());
|
||||
return node;
|
||||
}
|
||||
@ -411,7 +412,7 @@ namespace MWRender
|
||||
mMinSizeCostMultiplier = Settings::Manager::getFloat("object paging min size cost multiplier", "Terrain");
|
||||
}
|
||||
|
||||
osg::ref_ptr<osg::Node> ObjectPaging::createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile)
|
||||
osg::ref_ptr<osg::Node> 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));
|
||||
|
||||
@ -420,7 +421,8 @@ namespace MWRender
|
||||
|
||||
std::map<ESM::RefNum, ESM::CellRef> refs;
|
||||
ESM::ReadersCache readers;
|
||||
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
|
||||
const auto& world = MWBase::Environment::get().getWorld();
|
||||
const auto& store = world->getStore();
|
||||
|
||||
for (int cellX = startCell.x(); cellX < startCell.x() + size; ++cellX)
|
||||
{
|
||||
@ -548,6 +550,17 @@ namespace MWRender
|
||||
}
|
||||
}
|
||||
|
||||
if (!activeGrid)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mLODNameCacheMutex);
|
||||
LODNameCacheKey key{ model, lod };
|
||||
LODNameCache::const_iterator found = mLODNameCache.lower_bound(key);
|
||||
if (found != mLODNameCache.end() && found->first == key)
|
||||
model = found->second;
|
||||
else
|
||||
model = mLODNameCache.insert(found, { key, Misc::ResourceHelpers::getLODMeshName(world->getESMVersions()[ref.mRefNum.mContentFile], model, mSceneManager->getVFS(), lod) })->second;
|
||||
}
|
||||
|
||||
osg::ref_ptr<const osg::Node> cnode = mSceneManager->getTemplate(model, false);
|
||||
|
||||
if (activeGrid)
|
||||
|
@ -29,7 +29,7 @@ namespace MWRender
|
||||
|
||||
osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, unsigned char lod, unsigned int lodFlags, bool activeGrid, const osg::Vec3f& viewPoint, bool compile) override;
|
||||
|
||||
osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile);
|
||||
osg::ref_ptr<osg::Node> createChunk(float size, const osg::Vec2f& center, bool activeGrid, const osg::Vec3f& viewPoint, bool compile, unsigned char lod);
|
||||
|
||||
unsigned int getNodeMask() override;
|
||||
|
||||
@ -75,6 +75,11 @@ namespace MWRender
|
||||
std::mutex mSizeCacheMutex;
|
||||
typedef std::map<ESM::RefNum, float> SizeCache;
|
||||
SizeCache mSizeCache;
|
||||
|
||||
std::mutex mLODNameCacheMutex;
|
||||
typedef std::pair<std::string, unsigned char> LODNameCacheKey; //Key: mesh name, lod level
|
||||
typedef std::map<LODNameCacheKey, std::string> LODNameCache; //Cache: key, mesh name to use
|
||||
LODNameCache mLODNameCache;
|
||||
};
|
||||
|
||||
class RefnumMarker : public osg::Object
|
||||
|
@ -7,11 +7,12 @@
|
||||
namespace MWWorld
|
||||
{
|
||||
|
||||
EsmLoader::EsmLoader(MWWorld::ESMStore& store, ESM::ReadersCache& readers, ToUTF8::Utf8Encoder* encoder)
|
||||
EsmLoader::EsmLoader(MWWorld::ESMStore& store, ESM::ReadersCache& readers, ToUTF8::Utf8Encoder* encoder, std::vector<int>& esmVersions)
|
||||
: mReaders(readers)
|
||||
, mStore(store)
|
||||
, mEncoder(encoder)
|
||||
, mDialogue(nullptr) // A content file containing INFO records without a DIAL record appends them to the previous file's dialogue
|
||||
, mESMVersions(esmVersions)
|
||||
{
|
||||
}
|
||||
|
||||
@ -32,6 +33,7 @@ void EsmLoader::load(const boost::filesystem::path& filepath, int& index, Loadin
|
||||
+ ", but it is not available or has been loaded in the wrong order. "
|
||||
"Please run the launcher to fix this issue.");
|
||||
|
||||
mESMVersions[index] = reader->getVer();
|
||||
mStore.load(*reader, listener, mDialogue);
|
||||
|
||||
if (!mMasterFileFormat.has_value() && (Misc::StringUtils::ciEndsWith(reader->getName(), ".esm")
|
||||
|
@ -23,7 +23,7 @@ class ESMStore;
|
||||
|
||||
struct EsmLoader : public ContentLoader
|
||||
{
|
||||
explicit EsmLoader(MWWorld::ESMStore& store, ESM::ReadersCache& readers, ToUTF8::Utf8Encoder* encoder);
|
||||
explicit EsmLoader(MWWorld::ESMStore& store, ESM::ReadersCache& readers, ToUTF8::Utf8Encoder* encoder, std::vector<int>& esmVersions);
|
||||
|
||||
std::optional<int> getMasterFileFormat() const { return mMasterFileFormat; }
|
||||
|
||||
@ -35,6 +35,7 @@ struct EsmLoader : public ContentLoader
|
||||
ToUTF8::Utf8Encoder* mEncoder;
|
||||
ESM::Dialogue* mDialogue;
|
||||
std::optional<int> mMasterFileFormat;
|
||||
std::vector<int>& mESMVersions;
|
||||
};
|
||||
|
||||
} /* namespace MWWorld */
|
||||
|
@ -162,6 +162,7 @@ namespace MWWorld
|
||||
mLevitationEnabled(true), mGoToJail(false), mDaysInPrison(0),
|
||||
mPlayerTraveling(false), mPlayerInJail(false), mSpellPreloadTimer(0.f)
|
||||
{
|
||||
mESMVersions.resize(mContentFiles.size(), -1);
|
||||
Loading::Listener* listener = MWBase::Environment::get().getWindowManager()->getLoadingScreen();
|
||||
listener->loadingOn();
|
||||
|
||||
@ -620,6 +621,11 @@ namespace MWWorld
|
||||
return *mPlayer;
|
||||
}
|
||||
|
||||
const std::vector<int>& World::getESMVersions() const
|
||||
{
|
||||
return mESMVersions;
|
||||
}
|
||||
|
||||
const MWWorld::ESMStore& World::getStore() const
|
||||
{
|
||||
return mStore;
|
||||
@ -2933,7 +2939,7 @@ namespace MWWorld
|
||||
ToUTF8::Utf8Encoder* encoder, Loading::Listener* listener)
|
||||
{
|
||||
GameContentLoader gameContentLoader;
|
||||
EsmLoader esmLoader(mStore, mReaders, encoder);
|
||||
EsmLoader esmLoader(mStore, mReaders, encoder, mESMVersions);
|
||||
|
||||
gameContentLoader.addLoader(".esm", esmLoader);
|
||||
gameContentLoader.addLoader(".esp", esmLoader);
|
||||
|
@ -138,6 +138,8 @@ namespace MWWorld
|
||||
|
||||
float mSimulationTimeScale = 1.0;
|
||||
|
||||
std::vector<int> mESMVersions; //the versions of esm files
|
||||
|
||||
// not implemented
|
||||
World (const World&);
|
||||
World& operator= (const World&);
|
||||
@ -249,6 +251,8 @@ namespace MWWorld
|
||||
|
||||
const MWWorld::ESMStore& getStore() const override;
|
||||
|
||||
const std::vector<int>& getESMVersions() const override;
|
||||
|
||||
LocalScripts& getLocalScripts() override;
|
||||
|
||||
bool hasCellChanged() const override;
|
||||
|
@ -5,9 +5,14 @@
|
||||
|
||||
namespace Misc
|
||||
{
|
||||
inline size_t findExtension(std::string_view file)
|
||||
{
|
||||
return file.find_last_of('.');
|
||||
}
|
||||
|
||||
inline std::string_view getFileExtension(std::string_view file)
|
||||
{
|
||||
if (auto extPos = file.find_last_of('.'); extPos != std::string::npos)
|
||||
if (auto extPos = findExtension(file); extPos != std::string::npos)
|
||||
{
|
||||
file.remove_prefix(extPos + 1);
|
||||
return file;
|
||||
|
@ -4,9 +4,13 @@
|
||||
#include <string_view>
|
||||
#include <algorithm>
|
||||
|
||||
#include <components/esm/common.hpp>
|
||||
|
||||
#include <components/misc/pathhelpers.hpp>
|
||||
#include <components/misc/strings/lower.hpp>
|
||||
#include <components/misc/strings/algorithm.hpp>
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
#include <components/vfs/manager.hpp>
|
||||
|
||||
namespace
|
||||
@ -164,3 +168,49 @@ bool Misc::ResourceHelpers::isHiddenMarker(std::string_view id)
|
||||
{
|
||||
return Misc::StringUtils::ciEqual(id, "prisonmarker") || Misc::StringUtils::ciEqual(id, "divinemarker") || Misc::StringUtils::ciEqual(id, "templemarker") || Misc::StringUtils::ciEqual(id, "northmarker");
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string getLODMeshNameImpl(std::string resPath, const VFS::Manager* vfs, std::string_view pattern)
|
||||
{
|
||||
if (auto w = Misc::findExtension(resPath); w != std::string::npos)
|
||||
resPath.insert(w, pattern);
|
||||
return vfs->normalizeFilename(resPath);
|
||||
}
|
||||
|
||||
std::string getBestLODMeshName(std::string const& resPath, const VFS::Manager* vfs, std::string_view pattern)
|
||||
{
|
||||
if (const auto& result = getLODMeshNameImpl(resPath, vfs, pattern); vfs->exists(result))
|
||||
return result;
|
||||
return resPath;
|
||||
}
|
||||
}
|
||||
|
||||
std::string Misc::ResourceHelpers::getLODMeshName(int esmVersion, std::string resPath, const VFS::Manager* vfs, unsigned char lod)
|
||||
{
|
||||
const std::string distantMeshPattern = [&esmVersion] {
|
||||
switch (esmVersion)
|
||||
{
|
||||
case ESM::VER_120:
|
||||
case ESM::VER_130:
|
||||
return "_dist";
|
||||
case ESM::VER_080:
|
||||
case ESM::VER_100:
|
||||
return "_far";
|
||||
case ESM::VER_094:
|
||||
case ESM::VER_170:
|
||||
return "_lod";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}();
|
||||
for (char l = lod; l >= 0; --l)
|
||||
{
|
||||
std::stringstream patern;
|
||||
patern << distantMeshPattern << "_" << int(l);
|
||||
std::string const meshName = getBestLODMeshName(resPath, vfs, patern.str());
|
||||
if (meshName != resPath)
|
||||
return meshName;
|
||||
}
|
||||
return getBestLODMeshName(resPath, vfs, distantMeshPattern);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ namespace Misc
|
||||
|
||||
/// marker objects that have a hardcoded function in the game logic, should be hidden from the player
|
||||
bool isHiddenMarker(std::string_view id);
|
||||
std::string getLODMeshName(int esmVersion, std::string resPath, const VFS::Manager* vfs, unsigned char lod = 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user