1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-12-17 09:13:09 +00:00

Use normalized path for correctResourcePath and related functions

This commit is contained in:
elsid 2025-09-08 23:32:24 +02:00
parent e422c09820
commit b5196b2fd1
No known key found for this signature in database
GPG key ID: B845CB9FEE18AB40
38 changed files with 194 additions and 182 deletions

View file

@ -9,6 +9,10 @@ namespace Misc::ResourceHelpers
{ {
using namespace ::testing; using namespace ::testing;
constexpr VFS::Path::NormalizedView sound("sound");
constexpr VFS::Path::NormalizedView textures("textures");
constexpr VFS::Path::NormalizedView bookart("bookart");
TEST(MiscResourceHelpersCorrectSoundPath, shouldKeepWavExtensionIfExistsInVfs) TEST(MiscResourceHelpersCorrectSoundPath, shouldKeepWavExtensionIfExistsInVfs)
{ {
constexpr VFS::Path::NormalizedView path("sound/foo.wav"); constexpr VFS::Path::NormalizedView path("sound/foo.wav");
@ -36,8 +40,9 @@ namespace Misc::ResourceHelpers
TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtentionIfDoesNotExistInVfs) TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtentionIfDoesNotExistInVfs)
{ {
constexpr VFS::Path::NormalizedView path("sound/foo.wav");
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({}); const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
EXPECT_EQ(correctResourcePath({ { "sound" } }, "sound/foo.wav", vfs.get(), "mp3"), "sound/foo.mp3"); EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, "mp3"), "sound/foo.mp3");
} }
TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtentionIfBothExistInVfs) TEST(MiscResourceHelpersCorrectResourcePath, shouldFallbackToGivenExtentionIfBothExistInVfs)
@ -48,7 +53,7 @@ namespace Misc::ResourceHelpers
{ wav, nullptr }, { wav, nullptr },
{ mp3, nullptr }, { mp3, nullptr },
}); });
EXPECT_EQ(correctResourcePath({ { "sound" } }, wav.value(), vfs.get(), "mp3"), "sound/foo.mp3"); EXPECT_EQ(correctResourcePath({ { sound } }, wav, *vfs, "mp3"), "sound/foo.mp3");
} }
TEST(MiscResourceHelpersCorrectResourcePath, shouldKeepExtentionIfExistInVfs) TEST(MiscResourceHelpersCorrectResourcePath, shouldKeepExtentionIfExistInVfs)
@ -57,80 +62,58 @@ namespace Misc::ResourceHelpers
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({ const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
{ wav, nullptr }, { wav, nullptr },
}); });
EXPECT_EQ(correctResourcePath({ { "sound" } }, wav.value(), vfs.get(), "mp3"), "sound/foo.wav"); EXPECT_EQ(correctResourcePath({ { sound } }, wav, *vfs, "mp3"), "sound/foo.wav");
} }
TEST(MiscResourceHelpersCorrectResourcePath, shouldPrefixWithGivenTopDirectory) TEST(MiscResourceHelpersCorrectResourcePath, shouldPrefixWithGivenTopDirectory)
{ {
constexpr VFS::Path::NormalizedView path("foo.mp3");
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({}); const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
EXPECT_EQ(correctResourcePath({ { "sound" } }, "foo.mp3", vfs.get(), "mp3"), "sound/foo.mp3"); EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, "mp3"), "sound/foo.mp3");
} }
TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndKeepExtensionIfOriginalExistInVfs) TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndKeepExtensionIfOriginalExistInVfs)
{ {
constexpr VFS::Path::NormalizedView path("bookart/foo.a");
constexpr VFS::Path::NormalizedView a("textures/foo.a"); constexpr VFS::Path::NormalizedView a("textures/foo.a");
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({ const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
{ a, nullptr }, { a, nullptr },
}); });
EXPECT_EQ( EXPECT_EQ(correctResourcePath({ { textures, bookart } }, path, *vfs, "b"), "textures/foo.a");
correctResourcePath({ { "textures", "bookart" } }, "bookart/foo.a", vfs.get(), "b"), "textures/foo.a");
} }
TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndChangeExtensionIfFallbackExistInVfs) TEST(MiscResourceHelpersCorrectResourcePath, shouldChangeTopDirectoryAndChangeExtensionIfFallbackExistInVfs)
{ {
constexpr VFS::Path::NormalizedView path("bookart/foo.a");
constexpr VFS::Path::NormalizedView b("textures/foo.b"); constexpr VFS::Path::NormalizedView b("textures/foo.b");
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({ const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({
{ b, nullptr }, { b, nullptr },
}); });
EXPECT_EQ( EXPECT_EQ(correctResourcePath({ { textures, bookart } }, path, *vfs, "b"), "textures/foo.b");
correctResourcePath({ { "textures", "bookart" } }, "bookart/foo.a", vfs.get(), "b"), "textures/foo.b");
}
TEST(MiscResourceHelpersCorrectResourcePath, shouldLowerCase)
{
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
EXPECT_EQ(correctResourcePath({ { "sound" } }, "SOUND\\Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3");
}
TEST(MiscResourceHelpersCorrectResourcePath, shouldRemoveLeadingSlash)
{
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
EXPECT_EQ(correctResourcePath({ { "sound" } }, "\\SOUND\\Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3");
}
TEST(MiscResourceHelpersCorrectResourcePath, shouldRemoveDuplicateSlashes)
{
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
EXPECT_EQ(correctResourcePath({ { "sound" } }, "\\\\SOUND\\\\Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3");
}
TEST(MiscResourceHelpersCorrectResourcePath, shouldConvertToForwardSlash)
{
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
EXPECT_EQ(correctResourcePath({ { "sound" } }, "SOUND/Foo.MP3", vfs.get(), "mp3"), "sound/foo.mp3");
} }
TEST(MiscResourceHelpersCorrectResourcePath, shouldHandlePathEqualToDirectory) TEST(MiscResourceHelpersCorrectResourcePath, shouldHandlePathEqualToDirectory)
{ {
constexpr VFS::Path::NormalizedView path("sound");
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({}); const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
EXPECT_EQ(correctResourcePath({ { "sound" } }, "sound", vfs.get(), "mp3"), "sound/sound"); EXPECT_EQ(correctResourcePath({ { sound } }, path, *vfs, "mp3"), "sound/sound");
} }
struct MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix : TestWithParam<std::string> struct MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix : TestWithParam<VFS::Path::NormalizedView>
{ {
}; };
TEST_P(MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix, shouldMatchExpected) TEST_P(MiscResourceHelpersCorrectResourcePathShouldRemoveExtraPrefix, shouldMatchExpected)
{ {
const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({}); const std::unique_ptr<const VFS::Manager> vfs = TestingOpenMW::createTestVFS({});
EXPECT_EQ(correctResourcePath({ { "sound" } }, GetParam(), vfs.get(), "mp3"), "sound/foo.mp3"); EXPECT_EQ(correctResourcePath({ { sound } }, GetParam(), *vfs, "mp3"), "sound/foo.mp3");
} }
const std::vector<std::string> pathsWithPrefix = { const std::vector<VFS::Path::NormalizedView> pathsWithPrefix = {
"data/sound/foo.mp3", VFS::Path::NormalizedView("data/sound/foo.mp3"),
"data/notsound/sound/foo.mp3", VFS::Path::NormalizedView("data/notsound/sound/foo.mp3"),
"data/soundnot/sound/foo.mp3", VFS::Path::NormalizedView("data/soundnot/sound/foo.mp3"),
"data/notsoundnot/sound/foo.mp3", VFS::Path::NormalizedView("data/notsoundnot/sound/foo.mp3"),
}; };
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(

View file

@ -212,7 +212,7 @@ namespace MWGui
const ESM::BirthSign* birth = store.get<ESM::BirthSign>().find(mCurrentBirthId); const ESM::BirthSign* birth = store.get<ESM::BirthSign>().find(mCurrentBirthId);
mBirthImage->setImageTexture(Misc::ResourceHelpers::correctTexturePath( mBirthImage->setImageTexture(Misc::ResourceHelpers::correctTexturePath(
birth->mTexture, MWBase::Environment::get().getResourceSystem()->getVFS())); VFS::Path::toNormalized(birth->mTexture), *MWBase::Environment::get().getResourceSystem()->getVFS()));
std::vector<ESM::RefId> abilities, powers, spells; std::vector<ESM::RefId> abilities, powers, spells;

View file

@ -1196,8 +1196,8 @@ namespace MWGui
if (const auto* id = classId.getIf<ESM::StringRefId>()) if (const auto* id = classId.getIf<ESM::StringRefId>())
{ {
const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
classImage classImage = Misc::ResourceHelpers::correctTexturePath(
= Misc::ResourceHelpers::correctTexturePath("textures\\levelup\\" + id->getValue() + ".dds", vfs); VFS::Path::toNormalized("textures\\levelup\\" + id->getValue() + ".dds"), *vfs);
if (!vfs->exists(classImage)) if (!vfs->exists(classImage))
{ {
Log(Debug::Warning) << "No class image for " << classId << ", falling back to default"; Log(Debug::Warning) << "No class image for " << classId << ", falling back to default";

View file

@ -330,15 +330,16 @@ namespace MWGui::Formatting
if (auto heightIt = attr.find("height"); heightIt != attr.end()) if (auto heightIt = attr.find("height"); heightIt != attr.end())
height = MyGUI::utility::parseInt(heightIt->second); height = MyGUI::utility::parseInt(heightIt->second);
const std::string& src = srcIt->second; const std::string_view src = srcIt->second;
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
std::string correctedSrc; VFS::Path::Normalized correctedSrc;
constexpr std::string_view imgPrefix = "img://"; constexpr std::string_view imgPrefix = "img://";
if (src.starts_with(imgPrefix)) if (src.starts_with(imgPrefix))
{ {
correctedSrc = src.substr(imgPrefix.size(), src.size() - imgPrefix.size()); correctedSrc
= VFS::Path::toNormalized(src.substr(imgPrefix.size(), src.size() - imgPrefix.size()));
if (width == 0) if (width == 0)
{ {
width = 50; width = 50;
@ -351,7 +352,8 @@ namespace MWGui::Formatting
{ {
if (width == 0 || height == 0) if (width == 0 || height == 0)
continue; continue;
correctedSrc = Misc::ResourceHelpers::correctBookartPath(src, width, height, vfs); correctedSrc = Misc::ResourceHelpers::correctBookartPath(
VFS::Path::toNormalized(src), width, height, *vfs);
} }
if (!vfs->exists(correctedSrc)) if (!vfs->exists(correctedSrc))

View file

@ -386,9 +386,9 @@ namespace MWGui
std::replace(icon.begin(), icon.end(), '/', '\\'); std::replace(icon.begin(), icon.end(), '/', '\\');
size_t slashPos = icon.rfind('\\'); size_t slashPos = icon.rfind('\\');
icon.insert(slashPos + 1, "b_"); icon.insert(slashPos + 1, "b_");
icon = Misc::ResourceHelpers::correctIconPath( const VFS::Path::Normalized iconPath = Misc::ResourceHelpers::correctIconPath(
icon, MWBase::Environment::get().getResourceSystem()->getVFS()); VFS::Path::toNormalized(icon), *MWBase::Environment::get().getResourceSystem()->getVFS());
mSpellImage->setSpellIcon(icon); mSpellImage->setSpellIcon(iconPath);
} }
else else
mSpellImage->setSpellIcon({}); mSpellImage->setSpellIcon({});

View file

@ -136,16 +136,17 @@ namespace MWGui
void ItemWidget::setIcon(const MWWorld::Ptr& ptr) void ItemWidget::setIcon(const MWWorld::Ptr& ptr)
{ {
constexpr VFS::Path::NormalizedView defaultIcon("default icon.tga");
std::string_view icon = ptr.getClass().getInventoryIcon(ptr); std::string_view icon = ptr.getClass().getInventoryIcon(ptr);
if (icon.empty()) if (icon.empty())
icon = "default icon.tga"; icon = defaultIcon.value();
const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); const VFS::Manager* const vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
std::string invIcon = Misc::ResourceHelpers::correctIconPath(icon, vfs); std::string invIcon = Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(icon), *vfs);
if (!vfs->exists(invIcon)) if (!vfs->exists(invIcon))
{ {
Log(Debug::Error) << "Failed to open image: '" << invIcon Log(Debug::Error) << "Failed to open image: '" << invIcon << "' not found, falling back to '"
<< "' not found, falling back to 'default-icon.tga'"; << defaultIcon.value() << "'";
invIcon = Misc::ResourceHelpers::correctIconPath("default icon.tga", vfs); invIcon = Misc::ResourceHelpers::correctIconPath(defaultIcon, *vfs);
} }
setIcon(invIcon); setIcon(invIcon);
} }

View file

@ -325,7 +325,8 @@ namespace MWGui
std::string path = effect->mIcon; std::string path = effect->mIcon;
std::replace(path.begin(), path.end(), '/', '\\'); std::replace(path.begin(), path.end(), '/', '\\');
path.insert(path.rfind('\\') + 1, "b_"); path.insert(path.rfind('\\') + 1, "b_");
path = Misc::ResourceHelpers::correctIconPath(path, MWBase::Environment::get().getResourceSystem()->getVFS()); const VFS::Path::Normalized iconPath = Misc::ResourceHelpers::correctIconPath(
VFS::Path::toNormalized(path), *MWBase::Environment::get().getResourceSystem()->getVFS());
float scale = 1.f; float scale = 1.f;
MyGUI::ITexture* texture MyGUI::ITexture* texture
@ -335,7 +336,7 @@ namespace MWGui
const int diameter = static_cast<int>(44 * scale); const int diameter = static_cast<int>(44 * scale);
mSelected->button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(0, 0, diameter, diameter)); mSelected->button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(0, 0, diameter, diameter));
mSelected->button->setIcon(path); mSelected->button->setIcon(iconPath);
if (mMagicSelectionDialog) if (mMagicSelectionDialog)
mMagicSelectionDialog->setVisible(false); mMagicSelectionDialog->setVisible(false);

View file

@ -220,7 +220,7 @@ namespace MWGui
void EditEffectDialog::setMagicEffect(const ESM::MagicEffect* effect) void EditEffectDialog::setMagicEffect(const ESM::MagicEffect* effect)
{ {
mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath( mEffectImage->setImageTexture(Misc::ResourceHelpers::correctIconPath(
effect->mIcon, MWBase::Environment::get().getResourceSystem()->getVFS())); VFS::Path::toNormalized(effect->mIcon), *MWBase::Environment::get().getResourceSystem()->getVFS()));
mEffectName->setCaptionWithReplacing("#{" + ESM::MagicEffect::indexToGmstString(effect->mIndex) + "}"); mEffectName->setCaptionWithReplacing("#{" + ESM::MagicEffect::indexToGmstString(effect->mIndex) + "}");

View file

@ -150,8 +150,9 @@ namespace MWGui
"ImageBox", MyGUI::IntCoord(w, 2, 16, 16), MyGUI::Align::Default); "ImageBox", MyGUI::IntCoord(w, 2, 16, 16), MyGUI::Align::Default);
mWidgetMap[effectId] = image; mWidgetMap[effectId] = image;
image->setImageTexture(Misc::ResourceHelpers::correctIconPath( image->setImageTexture(
effect->mIcon, MWBase::Environment::get().getResourceSystem()->getVFS())); Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(effect->mIcon),
*MWBase::Environment::get().getResourceSystem()->getVFS()));
const std::string& name = ESM::MagicEffect::indexToGmstString(effectId); const std::string& name = ESM::MagicEffect::indexToGmstString(effectId);

View file

@ -447,8 +447,8 @@ namespace MWGui
const int maximumWidth = MyGUI::RenderManager::getInstance().getViewSize().width - imageCaptionHPadding * 2; const int maximumWidth = MyGUI::RenderManager::getInstance().getViewSize().width - imageCaptionHPadding * 2;
const std::string realImage const VFS::Path::Normalized realImage = Misc::ResourceHelpers::correctIconPath(
= Misc::ResourceHelpers::correctIconPath(image, MWBase::Environment::get().getResourceSystem()->getVFS()); VFS::Path::toNormalized(image), *MWBase::Environment::get().getResourceSystem()->getVFS());
Gui::EditBox* captionWidget = mDynamicToolTipBox->createWidget<Gui::EditBox>( Gui::EditBox* captionWidget = mDynamicToolTipBox->createWidget<Gui::EditBox>(
"NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption"); "NormalText", MyGUI::IntCoord(0, 0, 300, 300), MyGUI::Align::Left | MyGUI::Align::Top, "ToolTipCaption");
@ -887,8 +887,8 @@ namespace MWGui
widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipType", "Layout");
widget->setUserString("ToolTipLayout", "BirthSignToolTip"); widget->setUserString("ToolTipLayout", "BirthSignToolTip");
widget->setUserString( widget->setUserString("ImageTexture_BirthSignImage",
"ImageTexture_BirthSignImage", Misc::ResourceHelpers::correctTexturePath(sign->mTexture, vfs)); Misc::ResourceHelpers::correctTexturePath(VFS::Path::toNormalized(sign->mTexture), *vfs));
widget->setUserString("Caption_BirthSignName", sign->mName); widget->setUserString("Caption_BirthSignName", sign->mName);
widget->setUserString("Caption_BirthSignDescription", sign->mDescription); widget->setUserString("Caption_BirthSignDescription", sign->mDescription);
@ -962,7 +962,8 @@ namespace MWGui
std::string icon = effect->mIcon; std::string icon = effect->mIcon;
icon.insert(icon.rfind('\\') + 1, "b_"); icon.insert(icon.rfind('\\') + 1, "b_");
icon = Misc::ResourceHelpers::correctIconPath(icon, MWBase::Environment::get().getResourceSystem()->getVFS()); const VFS::Path::Normalized iconPath = Misc::ResourceHelpers::correctIconPath(
VFS::Path::toNormalized(icon), *MWBase::Environment::get().getResourceSystem()->getVFS());
widget->setUserString("ToolTipType", "Layout"); widget->setUserString("ToolTipType", "Layout");
widget->setUserString("ToolTipLayout", "MagicEffectToolTip"); widget->setUserString("ToolTipLayout", "MagicEffectToolTip");
@ -972,6 +973,6 @@ namespace MWGui
"#{sSchool}: " "#{sSchool}: "
+ MyGUI::TextIterator::toTagsString( + MyGUI::TextIterator::toTagsString(
store->get<ESM::Skill>().find(effect->mData.mSchool)->mSchool->mName)); store->get<ESM::Skill>().find(effect->mData.mSchool)->mSchool->mName));
widget->setUserString("ImageTexture_MagicEffectImage", icon); widget->setUserString("ImageTexture_MagicEffectImage", iconPath);
} }
} }

View file

@ -477,7 +477,7 @@ namespace MWGui::Widgets
mRequestedWidth = mTextWidget->getTextSize().width + sIconOffset; mRequestedWidth = mTextWidget->getTextSize().width + sIconOffset;
mImageWidget->setImageTexture(Misc::ResourceHelpers::correctIconPath( mImageWidget->setImageTexture(Misc::ResourceHelpers::correctIconPath(
magicEffect->mIcon, MWBase::Environment::get().getResourceSystem()->getVFS())); VFS::Path::toNormalized(magicEffect->mIcon), *MWBase::Environment::get().getResourceSystem()->getVFS()));
} }
MWSpellEffect::~MWSpellEffect() {} MWSpellEffect::~MWSpellEffect() {}

View file

@ -36,7 +36,7 @@ namespace MWLua
= sol::readonly_property([](const ESM::BirthSign& rec) -> std::string_view { return rec.mDescription; }); = sol::readonly_property([](const ESM::BirthSign& rec) -> std::string_view { return rec.mDescription; });
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
signT["texture"] = sol::readonly_property([vfs](const ESM::BirthSign& rec) -> std::string { signT["texture"] = sol::readonly_property([vfs](const ESM::BirthSign& rec) -> std::string {
return Misc::ResourceHelpers::correctTexturePath(rec.mTexture, vfs); return Misc::ResourceHelpers::correctTexturePath(VFS::Path::toNormalized(rec.mTexture), *vfs);
}); });
signT["spells"] = sol::readonly_property([lua](const ESM::BirthSign& rec) -> sol::table { signT["spells"] = sol::readonly_property([lua](const ESM::BirthSign& rec) -> sol::table {
return createReadOnlyRefIdTable(lua, rec.mPowers.mList); return createReadOnlyRefIdTable(lua, rec.mPowers.mList);

View file

@ -394,7 +394,7 @@ namespace MWLua
}); });
magicEffectT["icon"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string { magicEffectT["icon"] = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string {
auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS(); auto vfs = MWBase::Environment::get().getResourceSystem()->getVFS();
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
magicEffectT["particle"] magicEffectT["particle"]
= sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string_view { return rec.mParticle; }); = sol::readonly_property([](const ESM::MagicEffect& rec) -> std::string_view { return rec.mParticle; });

View file

@ -649,7 +649,7 @@ namespace MWLua
attributeT["description"] attributeT["description"]
= sol::readonly_property([](const ESM::Attribute& rec) -> std::string_view { return rec.mDescription; }); = sol::readonly_property([](const ESM::Attribute& rec) -> std::string_view { return rec.mDescription; });
attributeT["icon"] = sol::readonly_property([vfs](const ESM::Attribute& rec) -> std::string { attributeT["icon"] = sol::readonly_property([vfs](const ESM::Attribute& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
sol::table skills(lua, sol::create); sol::table skills(lua, sol::create);
@ -669,7 +669,7 @@ namespace MWLua
return ESM::Class::specializationIndexToLuaId.at(rec.mData.mSpecialization); return ESM::Class::specializationIndexToLuaId.at(rec.mData.mSpecialization);
}); });
skillT["icon"] = sol::readonly_property([vfs](const ESM::Skill& rec) -> std::string { skillT["icon"] = sol::readonly_property([vfs](const ESM::Skill& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
skillT["school"] = sol::readonly_property([](const ESM::Skill& rec) -> const ESM::MagicSchool* { skillT["school"] = sol::readonly_property([](const ESM::Skill& rec) -> const ESM::MagicSchool* {
if (!rec.mSchool) if (!rec.mSchool)

View file

@ -45,7 +45,7 @@ namespace MWLua
record["mwscript"] record["mwscript"]
= sol::readonly_property([](const ESM::Apparatus& rec) -> ESM::RefId { return rec.mScript; }); = sol::readonly_property([](const ESM::Apparatus& rec) -> ESM::RefId { return rec.mScript; });
record["icon"] = sol::readonly_property([vfs](const ESM::Apparatus& rec) -> std::string { record["icon"] = sol::readonly_property([vfs](const ESM::Apparatus& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
record["type"] = sol::readonly_property([](const ESM::Apparatus& rec) -> int { return rec.mData.mType; }); record["type"] = sol::readonly_property([](const ESM::Apparatus& rec) -> int { return rec.mData.mType; });
record["value"] = sol::readonly_property([](const ESM::Apparatus& rec) -> int { return rec.mData.mValue; }); record["value"] = sol::readonly_property([](const ESM::Apparatus& rec) -> int { return rec.mData.mValue; });

View file

@ -100,7 +100,7 @@ namespace MWLua
record["name"] = sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mName; }); record["name"] = sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mName; });
addModelProperty(record); addModelProperty(record);
record["icon"] = sol::readonly_property([vfs](const ESM::Armor& rec) -> std::string { record["icon"] = sol::readonly_property([vfs](const ESM::Armor& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
record["enchant"] = sol::readonly_property([](const ESM::Armor& rec) -> ESM::RefId { return rec.mEnchant; }); record["enchant"] = sol::readonly_property([](const ESM::Armor& rec) -> ESM::RefId { return rec.mEnchant; });
record["mwscript"] = sol::readonly_property([](const ESM::Armor& rec) -> ESM::RefId { return rec.mScript; }); record["mwscript"] = sol::readonly_property([](const ESM::Armor& rec) -> ESM::RefId { return rec.mScript; });

View file

@ -108,7 +108,7 @@ namespace MWLua
addModelProperty(record); addModelProperty(record);
record["mwscript"] = sol::readonly_property([](const ESM::Book& rec) -> ESM::RefId { return rec.mScript; }); record["mwscript"] = sol::readonly_property([](const ESM::Book& rec) -> ESM::RefId { return rec.mScript; });
record["icon"] = sol::readonly_property([vfs](const ESM::Book& rec) -> std::string { record["icon"] = sol::readonly_property([vfs](const ESM::Book& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
record["text"] = sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mText; }); record["text"] = sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mText; });
record["enchant"] = sol::readonly_property([](const ESM::Book& rec) -> ESM::RefId { return rec.mEnchant; }); record["enchant"] = sol::readonly_property([](const ESM::Book& rec) -> ESM::RefId { return rec.mEnchant; });

View file

@ -95,7 +95,7 @@ namespace MWLua
record["name"] = sol::readonly_property([](const ESM::Clothing& rec) -> std::string { return rec.mName; }); record["name"] = sol::readonly_property([](const ESM::Clothing& rec) -> std::string { return rec.mName; });
addModelProperty(record); addModelProperty(record);
record["icon"] = sol::readonly_property([vfs](const ESM::Clothing& rec) -> std::string { record["icon"] = sol::readonly_property([vfs](const ESM::Clothing& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
record["enchant"] = sol::readonly_property([](const ESM::Clothing& rec) -> ESM::RefId { return rec.mEnchant; }); record["enchant"] = sol::readonly_property([](const ESM::Clothing& rec) -> ESM::RefId { return rec.mEnchant; });
record["mwscript"] = sol::readonly_property([](const ESM::Clothing& rec) -> ESM::RefId { return rec.mScript; }); record["mwscript"] = sol::readonly_property([](const ESM::Clothing& rec) -> ESM::RefId { return rec.mScript; });

View file

@ -37,7 +37,7 @@ namespace MWLua
record["mwscript"] record["mwscript"]
= sol::readonly_property([](const ESM::Ingredient& rec) -> ESM::RefId { return rec.mScript; }); = sol::readonly_property([](const ESM::Ingredient& rec) -> ESM::RefId { return rec.mScript; });
record["icon"] = sol::readonly_property([vfs](const ESM::Ingredient& rec) -> std::string { record["icon"] = sol::readonly_property([vfs](const ESM::Ingredient& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
record["weight"] record["weight"]
= sol::readonly_property([](const ESM::Ingredient& rec) -> float { return rec.mData.mWeight; }); = sol::readonly_property([](const ESM::Ingredient& rec) -> float { return rec.mData.mWeight; });

View file

@ -101,7 +101,7 @@ namespace MWLua
record["name"] = sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mName; }); record["name"] = sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mName; });
addModelProperty(record); addModelProperty(record);
record["icon"] = sol::readonly_property([vfs](const ESM::Light& rec) -> std::string { record["icon"] = sol::readonly_property([vfs](const ESM::Light& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
record["sound"] record["sound"]
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mSound.serializeText(); }); = sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mSound.serializeText(); });

View file

@ -35,7 +35,7 @@ namespace MWLua
addModelProperty(record); addModelProperty(record);
record["mwscript"] = sol::readonly_property([](const ESM::Lockpick& rec) -> ESM::RefId { return rec.mScript; }); record["mwscript"] = sol::readonly_property([](const ESM::Lockpick& rec) -> ESM::RefId { return rec.mScript; });
record["icon"] = sol::readonly_property([vfs](const ESM::Lockpick& rec) -> std::string { record["icon"] = sol::readonly_property([vfs](const ESM::Lockpick& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
record["maxCondition"] record["maxCondition"]
= sol::readonly_property([](const ESM::Lockpick& rec) -> int { return rec.mData.mUses; }); = sol::readonly_property([](const ESM::Lockpick& rec) -> int { return rec.mData.mUses; });

View file

@ -85,7 +85,7 @@ namespace MWLua
record["mwscript"] record["mwscript"]
= sol::readonly_property([](const ESM::Miscellaneous& rec) -> ESM::RefId { return rec.mScript; }); = sol::readonly_property([](const ESM::Miscellaneous& rec) -> ESM::RefId { return rec.mScript; });
record["icon"] = sol::readonly_property([vfs](const ESM::Miscellaneous& rec) -> std::string { record["icon"] = sol::readonly_property([vfs](const ESM::Miscellaneous& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
record["isKey"] = sol::readonly_property( record["isKey"] = sol::readonly_property(
[](const ESM::Miscellaneous& rec) -> bool { return rec.mData.mFlags & ESM::Miscellaneous::Key; }); [](const ESM::Miscellaneous& rec) -> bool { return rec.mData.mFlags & ESM::Miscellaneous::Key; });

View file

@ -83,7 +83,7 @@ namespace MWLua
record["name"] = sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mName; }); record["name"] = sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mName; });
addModelProperty(record); addModelProperty(record);
record["icon"] = sol::readonly_property([vfs](const ESM::Potion& rec) -> std::string { record["icon"] = sol::readonly_property([vfs](const ESM::Potion& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
record["mwscript"] = sol::readonly_property([](const ESM::Potion& rec) -> ESM::RefId { return rec.mScript; }); record["mwscript"] = sol::readonly_property([](const ESM::Potion& rec) -> ESM::RefId { return rec.mScript; });
record["weight"] = sol::readonly_property([](const ESM::Potion& rec) -> float { return rec.mData.mWeight; }); record["weight"] = sol::readonly_property([](const ESM::Potion& rec) -> float { return rec.mData.mWeight; });

View file

@ -35,7 +35,7 @@ namespace MWLua
addModelProperty(record); addModelProperty(record);
record["mwscript"] = sol::readonly_property([](const ESM::Probe& rec) -> ESM::RefId { return rec.mScript; }); record["mwscript"] = sol::readonly_property([](const ESM::Probe& rec) -> ESM::RefId { return rec.mScript; });
record["icon"] = sol::readonly_property([vfs](const ESM::Probe& rec) -> std::string { record["icon"] = sol::readonly_property([vfs](const ESM::Probe& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
record["maxCondition"] = sol::readonly_property([](const ESM::Probe& rec) -> int { return rec.mData.mUses; }); record["maxCondition"] = sol::readonly_property([](const ESM::Probe& rec) -> int { return rec.mData.mUses; });
record["value"] = sol::readonly_property([](const ESM::Probe& rec) -> int { return rec.mData.mValue; }); record["value"] = sol::readonly_property([](const ESM::Probe& rec) -> int { return rec.mData.mValue; });

View file

@ -35,7 +35,7 @@ namespace MWLua
addModelProperty(record); addModelProperty(record);
record["mwscript"] = sol::readonly_property([](const ESM::Repair& rec) -> ESM::RefId { return rec.mScript; }); record["mwscript"] = sol::readonly_property([](const ESM::Repair& rec) -> ESM::RefId { return rec.mScript; });
record["icon"] = sol::readonly_property([vfs](const ESM::Repair& rec) -> std::string { record["icon"] = sol::readonly_property([vfs](const ESM::Repair& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
record["maxCondition"] = sol::readonly_property([](const ESM::Repair& rec) -> int { return rec.mData.mUses; }); record["maxCondition"] = sol::readonly_property([](const ESM::Repair& rec) -> int { return rec.mData.mUses; });
record["value"] = sol::readonly_property([](const ESM::Repair& rec) -> int { return rec.mData.mValue; }); record["value"] = sol::readonly_property([](const ESM::Repair& rec) -> int { return rec.mData.mValue; });

View file

@ -133,7 +133,7 @@ namespace MWLua
record["name"] = sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mName; }); record["name"] = sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mName; });
addModelProperty(record); addModelProperty(record);
record["icon"] = sol::readonly_property([vfs](const ESM::Weapon& rec) -> std::string { record["icon"] = sol::readonly_property([vfs](const ESM::Weapon& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs); return Misc::ResourceHelpers::correctIconPath(VFS::Path::toNormalized(rec.mIcon), *vfs);
}); });
record["enchant"] = sol::readonly_property([](const ESM::Weapon& rec) -> ESM::RefId { return rec.mEnchant; }); record["enchant"] = sol::readonly_property([](const ESM::Weapon& rec) -> ESM::RefId { return rec.mEnchant; });
record["mwscript"] = sol::readonly_property([](const ESM::Weapon& rec) -> ESM::RefId { return rec.mScript; }); record["mwscript"] = sol::readonly_property([](const ESM::Weapon& rec) -> ESM::RefId { return rec.mScript; });

View file

@ -155,8 +155,9 @@ namespace MWLua
weatherT["cloudSpeed"] = sol::property([](const MWWorld::Weather& w) { return w.mCloudSpeed; }, weatherT["cloudSpeed"] = sol::property([](const MWWorld::Weather& w) { return w.mCloudSpeed; },
[](MWWorld::Weather& w, const FiniteFloat cloudSpeed) { w.mCloudSpeed = cloudSpeed; }); [](MWWorld::Weather& w, const FiniteFloat cloudSpeed) { w.mCloudSpeed = cloudSpeed; });
weatherT["cloudTexture"] = sol::property( weatherT["cloudTexture"] = sol::property(
[vfs]( [vfs](const MWWorld::Weather& w) -> std::string {
const MWWorld::Weather& w) { return Misc::ResourceHelpers::correctTexturePath(w.mCloudTexture, vfs); }, return Misc::ResourceHelpers::correctTexturePath(VFS::Path::toNormalized(w.mCloudTexture), *vfs);
},
[](MWWorld::Weather& w, std::string_view cloudTexture) { w.mCloudTexture = cloudTexture; }); [](MWWorld::Weather& w, std::string_view cloudTexture) { w.mCloudTexture = cloudTexture; });
weatherT["cloudsMaximumPercent"] weatherT["cloudsMaximumPercent"]
= sol::property([](const MWWorld::Weather& w) { return w.mCloudsMaximumPercent; }, = sol::property([](const MWWorld::Weather& w) { return w.mCloudsMaximumPercent; },

View file

@ -1795,7 +1795,7 @@ namespace MWRender
// Notify that this animation has attached magic effects // Notify that this animation has attached magic effects
mHasMagicEffects = true; mHasMagicEffects = true;
overrideFirstRootTexture(texture, mResourceSystem, *node); overrideFirstRootTexture(VFS::Path::toNormalized(texture), mResourceSystem, *node);
} }
void Animation::removeEffect(std::string_view effectId) void Animation::removeEffect(std::string_view effectId)

View file

@ -55,9 +55,9 @@ namespace MWRender
node->accept(assignVisitor); node->accept(assignVisitor);
if (isMagicVFX) if (isMagicVFX)
overrideFirstRootTexture(textureOverride, mResourceSystem, *node); overrideFirstRootTexture(VFS::Path::toNormalized(textureOverride), mResourceSystem, *node);
else else
overrideTexture(textureOverride, mResourceSystem, *node); overrideTexture(VFS::Path::toNormalized(textureOverride), mResourceSystem, *node);
mParentNode->addChild(trans); mParentNode->addChild(trans);

View file

@ -763,7 +763,7 @@ namespace MWRender
mClouds = weather.mCloudTexture; mClouds = weather.mCloudTexture;
const VFS::Path::Normalized texture const VFS::Path::Normalized texture
= Misc::ResourceHelpers::correctTexturePath(mClouds, mSceneManager->getVFS()); = Misc::ResourceHelpers::correctTexturePath(VFS::Path::toNormalized(mClouds), *mSceneManager->getVFS());
osg::ref_ptr<osg::Texture2D> cloudTex osg::ref_ptr<osg::Texture2D> cloudTex
= new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture)); = new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture));
@ -785,8 +785,8 @@ namespace MWRender
if (!mNextClouds.empty()) if (!mNextClouds.empty())
{ {
const VFS::Path::Normalized texture const VFS::Path::Normalized texture = Misc::ResourceHelpers::correctTexturePath(
= Misc::ResourceHelpers::correctTexturePath(mNextClouds, mSceneManager->getVFS()); VFS::Path::toNormalized(mNextClouds), *mSceneManager->getVFS());
osg::ref_ptr<osg::Texture2D> cloudTex osg::ref_ptr<osg::Texture2D> cloudTex
= new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture)); = new osg::Texture2D(mSceneManager->getImageManager()->getImage(texture));

View file

@ -15,10 +15,9 @@ namespace MWRender
{ {
namespace namespace
{ {
class TextureOverrideVisitor : public osg::NodeVisitor struct TextureOverrideVisitor : osg::NodeVisitor
{ {
public: explicit TextureOverrideVisitor(VFS::Path::NormalizedView texture, Resource::ResourceSystem* resourcesystem)
TextureOverrideVisitor(std::string_view texture, Resource::ResourceSystem* resourcesystem)
: osg::NodeVisitor(TRAVERSE_ALL_CHILDREN) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN)
, mTexture(texture) , mTexture(texture)
, mResourcesystem(resourcesystem) , mResourcesystem(resourcesystem)
@ -35,23 +34,25 @@ namespace MWRender
} }
traverse(node); traverse(node);
} }
std::string_view mTexture;
VFS::Path::NormalizedView mTexture;
Resource::ResourceSystem* mResourcesystem; Resource::ResourceSystem* mResourcesystem;
}; };
} }
void overrideFirstRootTexture(std::string_view texture, Resource::ResourceSystem* resourceSystem, osg::Node& node) void overrideFirstRootTexture(
VFS::Path::NormalizedView texture, Resource::ResourceSystem* resourceSystem, osg::Node& node)
{ {
TextureOverrideVisitor overrideVisitor(texture, resourceSystem); TextureOverrideVisitor overrideVisitor(texture, resourceSystem);
node.accept(overrideVisitor); node.accept(overrideVisitor);
} }
void overrideTexture(std::string_view texture, Resource::ResourceSystem* resourceSystem, osg::Node& node) void overrideTexture(VFS::Path::NormalizedView texture, Resource::ResourceSystem* resourceSystem, osg::Node& node)
{ {
if (texture.empty()) if (texture.empty())
return; return;
const VFS::Path::Normalized correctedTexture const VFS::Path::Normalized correctedTexture
= Misc::ResourceHelpers::correctTexturePath(texture, resourceSystem->getVFS()); = Misc::ResourceHelpers::correctTexturePath(texture, *resourceSystem->getVFS());
// Not sure if wrap settings should be pulled from the overridden texture? // Not sure if wrap settings should be pulled from the overridden texture?
osg::ref_ptr<osg::Texture2D> tex osg::ref_ptr<osg::Texture2D> tex
= new osg::Texture2D(resourceSystem->getImageManager()->getImage(correctedTexture)); = new osg::Texture2D(resourceSystem->getImageManager()->getImage(correctedTexture));

View file

@ -1,6 +1,8 @@
#ifndef OPENMW_MWRENDER_UTIL_H #ifndef OPENMW_MWRENDER_UTIL_H
#define OPENMW_MWRENDER_UTIL_H #define OPENMW_MWRENDER_UTIL_H
#include <components/vfs/pathutil.hpp>
#include <osg/LightModel> #include <osg/LightModel>
#include <osg/NodeCallback> #include <osg/NodeCallback>
@ -21,9 +23,10 @@ namespace MWRender
// Overrides the texture of nodes in the mesh that had the same NiTexturingProperty as the first NiTexturingProperty // Overrides the texture of nodes in the mesh that had the same NiTexturingProperty as the first NiTexturingProperty
// of the .NIF file's root node, if it had a NiTexturingProperty. Used for applying "particle textures" to magic // of the .NIF file's root node, if it had a NiTexturingProperty. Used for applying "particle textures" to magic
// effects. // effects.
void overrideFirstRootTexture(std::string_view texture, Resource::ResourceSystem* resourceSystem, osg::Node& node); void overrideFirstRootTexture(
VFS::Path::NormalizedView texture, Resource::ResourceSystem* resourceSystem, osg::Node& node);
void overrideTexture(std::string_view texture, Resource::ResourceSystem* resourceSystem, osg::Node& node); void overrideTexture(VFS::Path::NormalizedView texture, Resource::ResourceSystem* resourceSystem, osg::Node& node);
// Node callback to entirely skip the traversal. // Node callback to entirely skip the traversal.
class NoTraverseCallback : public osg::NodeCallback class NoTraverseCallback : public osg::NodeCallback

View file

@ -19,6 +19,8 @@ namespace MWSound
{ {
namespace namespace
{ {
constexpr VFS::Path::NormalizedView soundDir("sound");
struct AudioParams struct AudioParams
{ {
float mAudioDefaultMinDistance; float mAudioDefaultMinDistance;
@ -192,7 +194,7 @@ namespace MWSound
max = std::max(min, max); max = std::max(min, max);
SoundBuffer& sfx = mSoundBuffers.emplace_back( SoundBuffer& sfx = mSoundBuffers.emplace_back(
Misc::ResourceHelpers::correctSoundPath(VFS::Path::Normalized(sound.mSound)), volume, min, max); Misc::ResourceHelpers::correctSoundPath(VFS::Path::toNormalized(sound.mSound)), volume, min, max);
mBufferNameMap.emplace(soundId, &sfx); mBufferNameMap.emplace(soundId, &sfx);
return &sfx; return &sfx;
@ -200,22 +202,24 @@ namespace MWSound
SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM4::Sound& sound) SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM4::Sound& sound)
{ {
std::string path = Misc::ResourceHelpers::correctResourcePath( VFS::Path::Normalized path
{ { "sound" } }, sound.mSoundFile, MWBase::Environment::get().getResourceSystem()->getVFS(), "mp3"); = Misc::ResourceHelpers::correctResourcePath({ { soundDir } }, VFS::Path::toNormalized(sound.mSoundFile),
*MWBase::Environment::get().getResourceSystem()->getVFS(), "mp3");
float volume = 1, min = 1, max = 255; // TODO: needs research float volume = 1, min = 1, max = 255; // TODO: needs research
SoundBuffer& sfx = mSoundBuffers.emplace_back(VFS::Path::Normalized(std::move(path)), volume, min, max); SoundBuffer& sfx = mSoundBuffers.emplace_back(std::move(path), volume, min, max);
mBufferNameMap.emplace(soundId, &sfx); mBufferNameMap.emplace(soundId, &sfx);
return &sfx; return &sfx;
} }
SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM4::SoundReference& sound) SoundBuffer* SoundBufferPool::insertSound(const ESM::RefId& soundId, const ESM4::SoundReference& sound)
{ {
std::string path = Misc::ResourceHelpers::correctResourcePath( VFS::Path::Normalized path
{ { "sound" } }, sound.mSoundFile, MWBase::Environment::get().getResourceSystem()->getVFS(), "mp3"); = Misc::ResourceHelpers::correctResourcePath({ { soundDir } }, VFS::Path::toNormalized(sound.mSoundFile),
*MWBase::Environment::get().getResourceSystem()->getVFS(), "mp3");
float volume = 1, min = 1, max = 255; // TODO: needs research float volume = 1, min = 1, max = 255; // TODO: needs research
// TODO: sound.mSoundId can link to another SoundReference, probably we will need to add additional lookups to // TODO: sound.mSoundId can link to another SoundReference, probably we will need to add additional lookups to
// ESMStore. // ESMStore.
SoundBuffer& sfx = mSoundBuffers.emplace_back(VFS::Path::Normalized(std::move(path)), volume, min, max); SoundBuffer& sfx = mSoundBuffers.emplace_back(std::move(path), volume, min, max);
mBufferNameMap.emplace(soundId, &sfx); mBufferNameMap.emplace(soundId, &sfx);
return &sfx; return &sfx;
} }

View file

@ -255,7 +255,7 @@ namespace MWWorld
SceneUtil::AssignControllerSourcesVisitor assignVisitor(state.mEffectAnimationTime); SceneUtil::AssignControllerSourcesVisitor assignVisitor(state.mEffectAnimationTime);
state.mNode->accept(assignVisitor); state.mNode->accept(assignVisitor);
MWRender::overrideFirstRootTexture(texture, mResourceSystem, *projectile); MWRender::overrideFirstRootTexture(VFS::Path::toNormalized(texture), mResourceSystem, *projectile);
} }
void ProjectileManager::update(State& state, float duration) void ProjectileManager::update(State& state, float duration)

View file

@ -386,7 +386,7 @@ namespace ESMTerrain
} }
} }
// this is needed due to MWs messed up texture handling // this is needed due to MWs messed up texture handling
return Misc::ResourceHelpers::correctTexturePath(texture, mVFS); return Misc::ResourceHelpers::correctTexturePath(VFS::Path::Normalized(texture), *mVFS);
} }
void Storage::getEsm4Blendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps, void Storage::getEsm4Blendmaps(float chunkSize, const osg::Vec2f& chunkCenter, ImageVector& blendmaps,

View file

@ -15,6 +15,12 @@
namespace namespace
{ {
constexpr VFS::Path::NormalizedView textures("textures");
constexpr VFS::Path::NormalizedView bookart("bookart");
constexpr VFS::Path::NormalizedView icons("icons");
constexpr VFS::Path::NormalizedView materials("materials");
constexpr std::string_view dds("dds");
bool changeExtension(std::string& path, std::string_view ext) bool changeExtension(std::string& path, std::string_view ext)
{ {
std::string::size_type pos = path.rfind('.'); std::string::size_type pos = path.rfind('.');
@ -26,14 +32,15 @@ namespace
return false; return false;
} }
std::size_t findDirectory(VFS::Path::NormalizedView path, std::string_view directory) std::size_t findDirectory(VFS::Path::NormalizedView path, VFS::Path::NormalizedView directory)
{ {
const std::string_view pathValue = path.value(); const std::string_view pathValue = path.value();
const std::size_t directorySize = directory.size(); const std::string_view directoryValue = directory.value();
const std::size_t directorySize = directoryValue.size();
for (std::size_t offset = 0, pathSize = pathValue.size(); offset < pathSize;) for (std::size_t offset = 0, pathSize = pathValue.size(); offset < pathSize;)
{ {
const std::size_t position = pathValue.find(directory, offset); const std::size_t position = pathValue.find(directoryValue, offset);
if (position == std::string_view::npos) if (position == std::string_view::npos)
return std::string_view::npos; return std::string_view::npos;
@ -50,13 +57,6 @@ namespace
return std::string_view::npos; return std::string_view::npos;
} }
VFS::Path::Normalized withPrefix(VFS::Path::NormalizedView path, std::string_view prefix)
{
VFS::Path::Normalized prefixed(prefix);
prefixed /= path;
return prefixed;
}
} }
bool Misc::ResourceHelpers::changeExtensionToDds(std::string& path) bool Misc::ResourceHelpers::changeExtensionToDds(std::string& path)
@ -65,51 +65,52 @@ bool Misc::ResourceHelpers::changeExtensionToDds(std::string& path)
} }
// If `ext` is not empty we first search file with extension `ext`, then if not found fallback to original extension. // If `ext` is not empty we first search file with extension `ext`, then if not found fallback to original extension.
std::string Misc::ResourceHelpers::correctResourcePath(std::span<const std::string_view> topLevelDirectories, VFS::Path::Normalized Misc::ResourceHelpers::correctResourcePath(
std::string_view resPath, const VFS::Manager* vfs, std::string_view ext) std::span<const VFS::Path::NormalizedView> topLevelDirectories, VFS::Path::NormalizedView resPath,
const VFS::Manager& vfs, std::string_view ext)
{ {
VFS::Path::Normalized correctedPath(resPath); VFS::Path::Normalized correctedPath;
// Handle top level directory // Handle top level directory
bool needsPrefix = true; bool needsPrefix = true;
for (const std::string_view potentialTopLevelDirectory : topLevelDirectories) for (const VFS::Path::NormalizedView potentialTopLevelDirectory : topLevelDirectories)
{ {
if (const std::size_t topLevelPos = findDirectory(correctedPath, potentialTopLevelDirectory); if (const std::size_t topLevelPos = findDirectory(resPath, potentialTopLevelDirectory);
topLevelPos != std::string::npos) topLevelPos != std::string::npos)
{ {
correctedPath = VFS::Path::Normalized(correctedPath.value().substr(topLevelPos)); correctedPath = VFS::Path::Normalized(resPath.value().substr(topLevelPos));
needsPrefix = false; needsPrefix = false;
break; break;
} }
} }
if (needsPrefix) if (needsPrefix)
correctedPath = withPrefix(correctedPath, topLevelDirectories.front()); correctedPath = topLevelDirectories.front() / resPath;
const VFS::Path::Normalized origExt = correctedPath; const VFS::Path::Normalized origExt = correctedPath;
// replace extension if `ext` is specified (used for .tga -> .dds, .wav -> .mp3) // replace extension if `ext` is specified (used for .tga -> .dds, .wav -> .mp3)
const bool isExtChanged = !ext.empty() && correctedPath.changeExtension(ext); const bool isExtChanged = !ext.empty() && correctedPath.changeExtension(ext);
if (vfs->exists(correctedPath)) if (vfs.exists(correctedPath))
return correctedPath; return correctedPath;
// fall back to original extension // fall back to original extension
if (isExtChanged && vfs->exists(origExt)) if (isExtChanged && vfs.exists(origExt))
return origExt; return origExt;
// fall back to a resource in the top level directory if it exists // fall back to a resource in the top level directory if it exists
{ {
const VFS::Path::Normalized fallback = withPrefix(correctedPath.filename(), topLevelDirectories.front()); const VFS::Path::Normalized fallback = topLevelDirectories.front() / correctedPath.filename();
if (vfs->exists(fallback)) if (vfs.exists(fallback))
return fallback; return fallback;
} }
if (isExtChanged) if (isExtChanged)
{ {
const VFS::Path::Normalized fallback = withPrefix(origExt.filename(), topLevelDirectories.front()); const VFS::Path::Normalized fallback = topLevelDirectories.front() / origExt.filename();
if (vfs->exists(fallback)) if (vfs.exists(fallback))
return fallback; return fallback;
} }
@ -120,33 +121,36 @@ std::string Misc::ResourceHelpers::correctResourcePath(std::span<const std::stri
// but all texture file name references were kept as .tga. So we pass ext=".dds" to all helpers // but all texture file name references were kept as .tga. So we pass ext=".dds" to all helpers
// looking for textures. // looking for textures.
std::string Misc::ResourceHelpers::correctTexturePath(std::string_view resPath, const VFS::Manager* vfs) VFS::Path::Normalized Misc::ResourceHelpers::correctTexturePath(
VFS::Path::NormalizedView resPath, const VFS::Manager& vfs)
{ {
return correctResourcePath({ { "textures", "bookart" } }, resPath, vfs, "dds"); return correctResourcePath({ { textures, bookart } }, resPath, vfs, dds);
} }
std::string Misc::ResourceHelpers::correctIconPath(std::string_view resPath, const VFS::Manager* vfs) VFS::Path::Normalized Misc::ResourceHelpers::correctIconPath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs)
{ {
return correctResourcePath({ { "icons" } }, resPath, vfs, "dds"); return correctResourcePath({ { icons } }, resPath, vfs, dds);
} }
std::string Misc::ResourceHelpers::correctBookartPath(std::string_view resPath, const VFS::Manager* vfs) VFS::Path::Normalized Misc::ResourceHelpers::correctBookartPath(
VFS::Path::NormalizedView resPath, const VFS::Manager& vfs)
{ {
return correctResourcePath({ { "bookart", "textures" } }, resPath, vfs, "dds"); return correctResourcePath({ { bookart, textures } }, resPath, vfs, dds);
} }
std::string Misc::ResourceHelpers::correctBookartPath( VFS::Path::Normalized Misc::ResourceHelpers::correctBookartPath(
std::string_view resPath, int width, int height, const VFS::Manager* vfs) VFS::Path::NormalizedView resPath, int width, int height, const VFS::Manager& vfs)
{ {
std::string image = correctBookartPath(resPath, vfs); VFS::Path::Normalized image = correctBookartPath(resPath, vfs);
// Apparently a bug with some morrowind versions, they reference the image without the size suffix. // Apparently a bug with some morrowind versions, they reference the image without the size suffix.
// So if the image isn't found, try appending the size. // So if the image isn't found, try appending the size.
if (!vfs->exists(image)) if (!vfs.exists(image))
{ {
std::stringstream str; std::stringstream str;
str << image.substr(0, image.rfind('.')) << "_" << width << "_" << height << image.substr(image.rfind('.')); str << image.view().substr(0, image.view().rfind('.')) << "_" << width << "_" << height
image = Misc::ResourceHelpers::correctBookartPath(str.str(), vfs); << image.view().substr(image.view().rfind('.'));
image = Misc::ResourceHelpers::correctBookartPath(VFS::Path::Normalized(std::move(str).str()), vfs);
} }
return image; return image;
@ -172,9 +176,10 @@ VFS::Path::Normalized Misc::ResourceHelpers::correctActorModelPath(
return mdlname; return mdlname;
} }
std::string Misc::ResourceHelpers::correctMaterialPath(std::string_view resPath, const VFS::Manager* vfs) VFS::Path::Normalized Misc::ResourceHelpers::correctMaterialPath(
VFS::Path::NormalizedView resPath, const VFS::Manager& vfs)
{ {
return correctResourcePath({ { "materials" } }, resPath, vfs); return correctResourcePath({ { materials } }, resPath, vfs);
} }
VFS::Path::Normalized Misc::ResourceHelpers::correctMeshPath(VFS::Path::NormalizedView resPath) VFS::Path::Normalized Misc::ResourceHelpers::correctMeshPath(VFS::Path::NormalizedView resPath)

View file

@ -25,16 +25,17 @@ namespace Misc
namespace ResourceHelpers namespace ResourceHelpers
{ {
bool changeExtensionToDds(std::string& path); bool changeExtensionToDds(std::string& path);
std::string correctResourcePath(std::span<const std::string_view> topLevelDirectories, std::string_view resPath, VFS::Path::Normalized correctResourcePath(std::span<const VFS::Path::NormalizedView> topLevelDirectories,
const VFS::Manager* vfs, std::string_view ext = {}); VFS::Path::NormalizedView resPath, const VFS::Manager& vfs, std::string_view ext = {});
std::string correctTexturePath(std::string_view resPath, const VFS::Manager* vfs); VFS::Path::Normalized correctTexturePath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs);
std::string correctIconPath(std::string_view resPath, const VFS::Manager* vfs); VFS::Path::Normalized correctIconPath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs);
std::string correctBookartPath(std::string_view resPath, const VFS::Manager* vfs); VFS::Path::Normalized correctBookartPath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs);
std::string correctBookartPath(std::string_view resPath, int width, int height, const VFS::Manager* vfs); VFS::Path::Normalized correctBookartPath(
VFS::Path::NormalizedView resPath, int width, int height, const VFS::Manager& vfs);
/// Use "xfoo.nif" instead of "foo.nif" if "xfoo.kf" is available /// Use "xfoo.nif" instead of "foo.nif" if "xfoo.kf" is available
/// Note that if "xfoo.nif" is actually unavailable, we can't fall back to "foo.nif". :( /// Note that if "xfoo.nif" is actually unavailable, we can't fall back to "foo.nif". :(
VFS::Path::Normalized correctActorModelPath(VFS::Path::NormalizedView resPath, const VFS::Manager* vfs); VFS::Path::Normalized correctActorModelPath(VFS::Path::NormalizedView resPath, const VFS::Manager* vfs);
std::string correctMaterialPath(std::string_view resPath, const VFS::Manager* vfs); VFS::Path::Normalized correctMaterialPath(VFS::Path::NormalizedView resPath, const VFS::Manager& vfs);
// Prepends "meshes/". // Prepends "meshes/".
VFS::Path::Normalized correctMeshPath(VFS::Path::NormalizedView resPath); VFS::Path::Normalized correctMeshPath(VFS::Path::NormalizedView resPath);

View file

@ -568,7 +568,7 @@ namespace NifOsg
if (st) if (st)
{ {
if (st->mExternal) if (st->mExternal)
return getTextureImage(st->mFile); return getTextureImage(VFS::Path::toNormalized(st->mFile));
if (!st->mData.empty()) if (!st->mData.empty())
return handleInternalTexture(st->mData.getPtr()); return handleInternalTexture(st->mData.getPtr());
@ -1065,13 +1065,12 @@ namespace NifOsg
} }
} }
osg::ref_ptr<osg::Image> getTextureImage(std::string_view path) const osg::ref_ptr<osg::Image> getTextureImage(VFS::Path::NormalizedView path) const
{ {
if (!mImageManager) if (!mImageManager)
return nullptr; return nullptr;
return mImageManager->getImage( return mImageManager->getImage(Misc::ResourceHelpers::correctTexturePath(path, *mImageManager->getVFS()));
VFS::Path::toNormalized(Misc::ResourceHelpers::correctTexturePath(path, mImageManager->getVFS())));
} }
static osg::ref_ptr<osg::Texture2D> attachTexture(const std::string& name, osg::ref_ptr<osg::Image> image, static osg::ref_ptr<osg::Texture2D> attachTexture(const std::string& name, osg::ref_ptr<osg::Image> image,
@ -1095,8 +1094,9 @@ namespace NifOsg
return texture2d; return texture2d;
} }
osg::ref_ptr<osg::Texture2D> attachExternalTexture(const std::string& name, const std::string& path, bool wrapS, osg::ref_ptr<osg::Texture2D> attachExternalTexture(const std::string& name, VFS::Path::NormalizedView path,
bool wrapT, unsigned int uvSet, osg::StateSet* stateset, std::vector<unsigned int>& boundTextures) const bool wrapS, bool wrapT, unsigned int uvSet, osg::StateSet* stateset,
std::vector<unsigned int>& boundTextures) const
{ {
return attachTexture(name, getTextureImage(path), wrapS, wrapT, uvSet, stateset, boundTextures); return attachTexture(name, getTextureImage(path), wrapS, wrapT, uvSet, stateset, boundTextures);
} }
@ -2223,18 +2223,19 @@ namespace NifOsg
} }
static Bgsm::MaterialFilePtr getShaderMaterial( static Bgsm::MaterialFilePtr getShaderMaterial(
std::string_view path, Resource::BgsmFileManager* materialManager) VFS::Path::NormalizedView path, Resource::BgsmFileManager* materialManager)
{ {
if (!materialManager) if (!materialManager)
return nullptr; return nullptr;
if (!Misc::StringUtils::ciEndsWith(path, ".bgem") && !Misc::StringUtils::ciEndsWith(path, ".bgsm")) if (!path.value().ends_with(".bgem") && !path.value().ends_with(".bgsm"))
return nullptr; return nullptr;
std::string normalizedPath = Misc::ResourceHelpers::correctMaterialPath(path, materialManager->getVFS()); const VFS::Path::Normalized normalizedPath
= Misc::ResourceHelpers::correctMaterialPath(path, *materialManager->getVFS());
try try
{ {
return materialManager->get(VFS::Path::Normalized(normalizedPath)); return materialManager->get(normalizedPath);
} }
catch (std::exception& e) catch (std::exception& e)
{ {
@ -2254,14 +2255,16 @@ namespace NifOsg
const Bgsm::BGSMFile* bgsm = static_cast<const Bgsm::BGSMFile*>(material); const Bgsm::BGSMFile* bgsm = static_cast<const Bgsm::BGSMFile*>(material);
if (!bgsm->mDiffuseMap.empty()) if (!bgsm->mDiffuseMap.empty())
attachExternalTexture( attachExternalTexture("diffuseMap", VFS::Path::toNormalized(bgsm->mDiffuseMap), wrapS, wrapT, uvSet,
"diffuseMap", bgsm->mDiffuseMap, wrapS, wrapT, uvSet, stateset, boundTextures); stateset, boundTextures);
if (!bgsm->mNormalMap.empty()) if (!bgsm->mNormalMap.empty())
attachExternalTexture("normalMap", bgsm->mNormalMap, wrapS, wrapT, uvSet, stateset, boundTextures); attachExternalTexture("normalMap", VFS::Path::toNormalized(bgsm->mNormalMap), wrapS, wrapT, uvSet,
stateset, boundTextures);
if (bgsm->mGlowMapEnabled && !bgsm->mGlowMap.empty()) if (bgsm->mGlowMapEnabled && !bgsm->mGlowMap.empty())
attachExternalTexture("emissiveMap", bgsm->mGlowMap, wrapS, wrapT, uvSet, stateset, boundTextures); attachExternalTexture("emissiveMap", VFS::Path::toNormalized(bgsm->mGlowMap), wrapS, wrapT, uvSet,
stateset, boundTextures);
if (bgsm->mTree) if (bgsm->mTree)
stateset->addUniform(new osg::Uniform("useTreeAnim", true)); stateset->addUniform(new osg::Uniform("useTreeAnim", true));
@ -2271,7 +2274,8 @@ namespace NifOsg
const Bgsm::BGEMFile* bgem = static_cast<const Bgsm::BGEMFile*>(material); const Bgsm::BGEMFile* bgem = static_cast<const Bgsm::BGEMFile*>(material);
if (!bgem->mBaseMap.empty()) if (!bgem->mBaseMap.empty())
attachExternalTexture("diffuseMap", bgem->mBaseMap, wrapS, wrapT, uvSet, stateset, boundTextures); attachExternalTexture("diffuseMap", VFS::Path::toNormalized(bgem->mBaseMap), wrapS, wrapT, uvSet,
stateset, boundTextures);
bool useFalloff = bgem->mFalloff; bool useFalloff = bgem->mFalloff;
stateset->addUniform(new osg::Uniform("useFalloff", useFalloff)); stateset->addUniform(new osg::Uniform("useFalloff", useFalloff));
@ -2386,16 +2390,16 @@ namespace NifOsg
switch (static_cast<Nif::BSShaderTextureSet::TextureType>(i)) switch (static_cast<Nif::BSShaderTextureSet::TextureType>(i))
{ {
case Nif::BSShaderTextureSet::TextureType::Base: case Nif::BSShaderTextureSet::TextureType::Base:
attachExternalTexture( attachExternalTexture("diffuseMap", VFS::Path::toNormalized(textureSet->mTextures[i]), wrapS,
"diffuseMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures); wrapT, uvSet, stateset, boundTextures);
break; break;
case Nif::BSShaderTextureSet::TextureType::Normal: case Nif::BSShaderTextureSet::TextureType::Normal:
attachExternalTexture( attachExternalTexture("normalMap", VFS::Path::toNormalized(textureSet->mTextures[i]), wrapS,
"normalMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures); wrapT, uvSet, stateset, boundTextures);
break; break;
case Nif::BSShaderTextureSet::TextureType::Glow: case Nif::BSShaderTextureSet::TextureType::Glow:
attachExternalTexture( attachExternalTexture("emissiveMap", VFS::Path::toNormalized(textureSet->mTextures[i]), wrapS,
"emissiveMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures); wrapT, uvSet, stateset, boundTextures);
break; break;
default: default:
{ {
@ -2578,8 +2582,8 @@ namespace NifOsg
if (!texprop->mFilename.empty()) if (!texprop->mFilename.empty())
{ {
const unsigned int uvSet = 0; const unsigned int uvSet = 0;
attachExternalTexture("diffuseMap", texprop->mFilename, texprop->wrapS(), texprop->wrapT(), attachExternalTexture("diffuseMap", VFS::Path::toNormalized(texprop->mFilename),
uvSet, stateset, boundTextures); texprop->wrapS(), texprop->wrapT(), uvSet, stateset, boundTextures);
} }
if (mBethVersion >= 27) if (mBethVersion >= 27)
{ {
@ -2599,7 +2603,8 @@ namespace NifOsg
node->setUserValue("shaderRequired", shaderRequired); node->setUserValue("shaderRequired", shaderRequired);
osg::StateSet* stateset = node->getOrCreateStateSet(); osg::StateSet* stateset = node->getOrCreateStateSet();
clearBoundTextures(stateset, boundTextures); clearBoundTextures(stateset, boundTextures);
if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName, mMaterialManager)) if (Bgsm::MaterialFilePtr material
= getShaderMaterial(VFS::Path::toNormalized(texprop->mName), mMaterialManager))
{ {
handleShaderMaterialNodeProperties(material.get(), stateset, boundTextures); handleShaderMaterialNodeProperties(material.get(), stateset, boundTextures);
break; break;
@ -2626,7 +2631,8 @@ namespace NifOsg
node->setUserValue("shaderRequired", shaderRequired); node->setUserValue("shaderRequired", shaderRequired);
osg::StateSet* stateset = node->getOrCreateStateSet(); osg::StateSet* stateset = node->getOrCreateStateSet();
clearBoundTextures(stateset, boundTextures); clearBoundTextures(stateset, boundTextures);
if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName, mMaterialManager)) if (Bgsm::MaterialFilePtr material
= getShaderMaterial(VFS::Path::toNormalized(texprop->mName), mMaterialManager))
{ {
handleShaderMaterialNodeProperties(material.get(), stateset, boundTextures); handleShaderMaterialNodeProperties(material.get(), stateset, boundTextures);
break; break;
@ -2635,8 +2641,8 @@ namespace NifOsg
{ {
const unsigned int uvSet = 0; const unsigned int uvSet = 0;
unsigned int texUnit = static_cast<unsigned>(boundTextures.size()); unsigned int texUnit = static_cast<unsigned>(boundTextures.size());
attachExternalTexture("diffuseMap", texprop->mSourceTexture, texprop->wrapS(), texprop->wrapT(), attachExternalTexture("diffuseMap", VFS::Path::toNormalized(texprop->mSourceTexture),
uvSet, stateset, boundTextures); texprop->wrapS(), texprop->wrapT(), uvSet, stateset, boundTextures);
{ {
osg::ref_ptr<osg::TexMat> texMat(new osg::TexMat); osg::ref_ptr<osg::TexMat> texMat(new osg::TexMat);
// This handles 20.2.0.7 UV settings like 4.0.0.2 UV settings (see NifOsg::UVController) // This handles 20.2.0.7 UV settings like 4.0.0.2 UV settings (see NifOsg::UVController)
@ -2845,7 +2851,8 @@ namespace NifOsg
case Nif::RC_BSLightingShaderProperty: case Nif::RC_BSLightingShaderProperty:
{ {
auto shaderprop = static_cast<const Nif::BSLightingShaderProperty*>(property); auto shaderprop = static_cast<const Nif::BSLightingShaderProperty*>(property);
if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName, mMaterialManager)) if (Bgsm::MaterialFilePtr shaderMat
= getShaderMaterial(VFS::Path::toNormalized(shaderprop->mName), mMaterialManager))
{ {
handleShaderMaterialDrawableProperties(shaderMat.get(), mat, *node, hasSortAlpha); handleShaderMaterialDrawableProperties(shaderMat.get(), mat, *node, hasSortAlpha);
if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting) if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting)
@ -2871,7 +2878,8 @@ namespace NifOsg
case Nif::RC_BSEffectShaderProperty: case Nif::RC_BSEffectShaderProperty:
{ {
auto shaderprop = static_cast<const Nif::BSEffectShaderProperty*>(property); auto shaderprop = static_cast<const Nif::BSEffectShaderProperty*>(property);
if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName, mMaterialManager)) if (Bgsm::MaterialFilePtr shaderMat
= getShaderMaterial(VFS::Path::toNormalized(shaderprop->mName), mMaterialManager))
{ {
handleShaderMaterialDrawableProperties(shaderMat.get(), mat, *node, hasSortAlpha); handleShaderMaterialDrawableProperties(shaderMat.get(), mat, *node, hasSortAlpha);
break; break;