diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index dacf7076ec..80ba39c465 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -28,16 +28,17 @@ #include #include #include + #include #include #include -#include - -#include #include +#include #include +#include +#include #include #include #include @@ -657,16 +658,24 @@ namespace MWRender return mKeyframes->mTextKeys; } - void Animation::loadAllAnimationsInFolder(const std::string& model, const std::string& baseModel) + void Animation::loadAdditionalAnimations(VFS::Path::NormalizedView model, const std::string& baseModel) { - std::string animationPath = model; - if (animationPath.find("meshes") == 0) - { - animationPath.replace(0, 6, "animations"); - } - animationPath.replace(animationPath.size() - 3, 3, "/"); + constexpr VFS::Path::NormalizedView meshes("meshes/"); + if (!model.value().starts_with(meshes.value())) + return; - for (const auto& name : mResourceSystem->getVFS()->getRecursiveDirectoryIterator(animationPath)) + std::string path(model.value()); + + constexpr VFS::Path::NormalizedView animations("animations/"); + path.replace(0, meshes.value().size(), animations.value()); + + const std::string::size_type extensionStart = path.find_last_of(VFS::Path::extensionSeparator); + if (extensionStart == std::string::npos) + return; + + path.replace(extensionStart, path.size() - extensionStart, "/"); + + for (const VFS::Path::Normalized& name : mResourceSystem->getVFS()->getRecursiveDirectoryIterator(path)) { if (Misc::getFileExtension(name) == "kf") { @@ -677,15 +686,15 @@ namespace MWRender void Animation::addAnimSource(std::string_view model, const std::string& baseModel) { - std::string kfname = Misc::StringUtils::lowerCase(model); + VFS::Path::Normalized kfname(model); - if (kfname.ends_with(".nif")) - kfname.replace(kfname.size() - 4, 4, ".kf"); + if (Misc::getFileExtension(kfname) == "nif") + kfname.changeExtension("kf"); addSingleAnimSource(kfname, baseModel); if (Settings::game().mUseAdditionalAnimSources) - loadAllAnimationsInFolder(kfname, baseModel); + loadAdditionalAnimations(kfname, baseModel); } std::shared_ptr Animation::addSingleAnimSource( diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 36a84ba2ab..d398c5b727 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -281,7 +282,7 @@ namespace MWRender */ void setObjectRoot(const std::string& model, bool forceskeleton, bool baseonly, bool isCreature); - void loadAllAnimationsInFolder(const std::string& model, const std::string& baseModel); + void loadAdditionalAnimations(VFS::Path::NormalizedView model, const std::string& baseModel); /** Adds the keyframe controllers in the specified model as a new animation source. * @note Later added animation sources have the highest priority when it comes to finding a particular diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 10bc239dd7..d6fdad4082 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -456,8 +456,8 @@ namespace MWRender mHeadModel.clear(); mHairModel.clear(); - const ESM::RefId& headName = isWerewolf ? ESM::RefId::stringRefId("WerewolfHead") : mNpc->mHead; - const ESM::RefId& hairName = isWerewolf ? ESM::RefId::stringRefId("WerewolfHair") : mNpc->mHair; + const ESM::RefId headName = isWerewolf ? ESM::RefId::stringRefId("WerewolfHead") : mNpc->mHead; + const ESM::RefId hairName = isWerewolf ? ESM::RefId::stringRefId("WerewolfHair") : mNpc->mHair; if (!headName.empty()) { @@ -647,7 +647,8 @@ namespace MWRender { const ESM::Light* light = part.get()->mBase; addOrReplaceIndividualPart(ESM::PRT_Shield, MWWorld::InventoryStore::Slot_CarriedLeft, 1, - Misc::ResourceHelpers::correctMeshPath(light->mModel), false, nullptr, true); + VFS::Path::toNormalized(Misc::ResourceHelpers::correctMeshPath(light->mModel)), false, nullptr, + true); if (mObjectParts[ESM::PRT_Shield]) addExtraLight(mObjectParts[ESM::PRT_Shield]->getNode()->asGroup(), SceneUtil::LightCommon(*light)); } @@ -667,7 +668,7 @@ namespace MWRender { if (const ESM::BodyPart* bodypart = parts[part]) addOrReplaceIndividualPart(static_cast(part), -1, 1, - Misc::ResourceHelpers::correctMeshPath(bodypart->mModel)); + VFS::Path::toNormalized(Misc::ResourceHelpers::correctMeshPath(bodypart->mModel))); } } @@ -675,10 +676,10 @@ namespace MWRender attachArrow(); } - PartHolderPtr NpcAnimation::insertBoundedPart(const std::string& model, std::string_view bonename, + PartHolderPtr NpcAnimation::insertBoundedPart(VFS::Path::NormalizedView model, std::string_view bonename, std::string_view bonefilter, bool enchantedGlow, osg::Vec4f* glowColor, bool isLight) { - osg::ref_ptr attached = attach(VFS::Path::toNormalized(model), bonename, bonefilter, isLight); + osg::ref_ptr attached = attach(model, bonename, bonefilter, isLight); if (enchantedGlow) mGlowUpdater = SceneUtil::addEnchantedGlow(attached, mResourceSystem, *glowColor); @@ -748,7 +749,7 @@ namespace MWRender } bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, - const std::string& mesh, bool enchantedGlow, osg::Vec4f* glowColor, bool isLight) + VFS::Path::NormalizedView mesh, bool enchantedGlow, osg::Vec4f* glowColor, bool isLight) { if (priority <= mPartPriorities[type]) return false; @@ -897,7 +898,8 @@ namespace MWRender if (bodypart) addOrReplaceIndividualPart(static_cast(part.mPart), group, priority, - Misc::ResourceHelpers::correctMeshPath(bodypart->mModel), enchantedGlow, glowColor); + VFS::Path::toNormalized(Misc::ResourceHelpers::correctMeshPath(bodypart->mModel)), enchantedGlow, + glowColor); else reserveIndividualPart((ESM::PartReferenceType)part.mPart, group, priority); } @@ -943,7 +945,7 @@ namespace MWRender if (weapon != inv.end()) { osg::Vec4f glowColor = weapon->getClass().getEnchantmentColor(*weapon); - std::string mesh = weapon->getClass().getCorrectedModel(*weapon); + const VFS::Path::Normalized mesh = weapon->getClass().getCorrectedModel(*weapon); addOrReplaceIndividualPart(ESM::PRT_Weapon, MWWorld::InventoryStore::Slot_CarriedRight, 1, mesh, !weapon->getClass().getEnchantment(*weapon).empty(), &glowColor); @@ -1003,7 +1005,7 @@ namespace MWRender if (show && iter != inv.end()) { osg::Vec4f glowColor = iter->getClass().getEnchantmentColor(*iter); - std::string mesh = iter->getClass().getCorrectedModel(*iter); + VFS::Path::Normalized mesh = iter->getClass().getCorrectedModel(*iter); // For shields we must try to use the body part model if (iter->getType() == ESM::Armor::sRecordId) { diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index a03ee28f3a..1f9a656d65 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -1,13 +1,14 @@ #ifndef GAME_RENDER_NPCANIMATION_H #define GAME_RENDER_NPCANIMATION_H +#include "actoranimation.hpp" #include "animation.hpp" +#include "weaponanimation.hpp" + +#include #include "../mwworld/inventorystore.hpp" -#include "actoranimation.hpp" -#include "weaponanimation.hpp" - #include namespace ESM @@ -50,8 +51,8 @@ namespace MWRender std::array mSounds; const ESM::NPC* mNpc; - std::string mHeadModel; - std::string mHairModel; + VFS::Path::Normalized mHeadModel; + VFS::Path::Normalized mHairModel; ViewMode mViewMode; bool mShowWeapons; bool mShowCarriedLeft; @@ -83,14 +84,15 @@ namespace MWRender NpcType getNpcType() const; - PartHolderPtr insertBoundedPart(const std::string& model, std::string_view bonename, + PartHolderPtr insertBoundedPart(VFS::Path::NormalizedView model, std::string_view bonename, std::string_view bonefilter, bool enchantedGlow, osg::Vec4f* glowColor, bool isLight); void removeIndividualPart(ESM::PartReferenceType type); void reserveIndividualPart(ESM::PartReferenceType type, int group, int priority); - bool addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, const std::string& mesh, - bool enchantedGlow = false, osg::Vec4f* glowColor = nullptr, bool isLight = false); + bool addOrReplaceIndividualPart(ESM::PartReferenceType type, int group, int priority, + VFS::Path::NormalizedView mesh, bool enchantedGlow = false, osg::Vec4f* glowColor = nullptr, + bool isLight = false); void removePartGroup(int group); void addPartGroup(int group, int priority, const std::vector& parts, bool enchantedGlow = false, osg::Vec4f* glowColor = nullptr); diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 2c398db07d..382453e713 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -189,7 +189,7 @@ namespace MWWorld float mRotateSpeed; }; - void ProjectileManager::createModel(State& state, const std::string& model, const osg::Vec3f& pos, + void ProjectileManager::createModel(State& state, VFS::Path::NormalizedView model, const osg::Vec3f& pos, const osg::Quat& orient, bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, const std::string& texture) { state.mNode = new osg::PositionAttitudeTransform; @@ -207,8 +207,7 @@ namespace MWWorld attachTo = rotateNode; } - osg::ref_ptr projectile - = mResourceSystem->getSceneManager()->getInstance(VFS::Path::toNormalized(model), attachTo); + osg::ref_ptr projectile = mResourceSystem->getSceneManager()->getInstance(model, attachTo); if (state.mIdMagic.size() > 1) { @@ -315,7 +314,7 @@ namespace MWWorld osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects); - auto model = ptr.getClass().getCorrectedModel(ptr); + VFS::Path::Normalized model = ptr.getClass().getCorrectedModel(ptr); createModel(state, model, pos, orient, true, true, lightDiffuseColor, texture); MWBase::SoundManager* sndMgr = MWBase::Environment::get().getSoundManager(); @@ -355,7 +354,7 @@ namespace MWWorld MWWorld::ManualRef ref(*MWBase::Environment::get().getESMStore(), projectile.getCellRef().getRefId()); MWWorld::Ptr ptr = ref.getPtr(); - const auto model = ptr.getClass().getCorrectedModel(ptr); + const VFS::Path::Normalized model = ptr.getClass().getCorrectedModel(ptr); createModel(state, model, pos, orient, false, false, osg::Vec4(0, 0, 0, 0)); if (!ptr.getClass().getEnchantment(ptr).empty()) SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr)); @@ -695,7 +694,7 @@ namespace MWWorld state.mAttackStrength = esm.mAttackStrength; state.mToDelete = false; - std::string model; + VFS::Path::Normalized model; try { MWWorld::ManualRef ref(*MWBase::Environment::get().getESMStore(), esm.mId); @@ -750,7 +749,7 @@ namespace MWWorld // file's effect list, which is already trimmed of non-projectile // effects. We need to use the stored value. - std::string model; + VFS::Path::Normalized model; try { MWWorld::ManualRef ref(*MWBase::Environment::get().getESMStore(), state.mIdMagic.at(0)); diff --git a/apps/openmw/mwworld/projectilemanager.hpp b/apps/openmw/mwworld/projectilemanager.hpp index 012e3b5922..3003136ffc 100644 --- a/apps/openmw/mwworld/projectilemanager.hpp +++ b/apps/openmw/mwworld/projectilemanager.hpp @@ -7,6 +7,7 @@ #include #include +#include #include "../mwbase/soundmanager.hpp" @@ -135,7 +136,7 @@ namespace MWWorld void moveProjectiles(float dt); void moveMagicBolts(float dt); - void createModel(State& state, const std::string& model, const osg::Vec3f& pos, const osg::Quat& orient, + void createModel(State& state, VFS::Path::NormalizedView model, const osg::Vec3f& pos, const osg::Quat& orient, bool rotate, bool createLight, osg::Vec4 lightDiffuseColor, const std::string& texture = ""); void update(State& state, float duration); diff --git a/components/sceneutil/util.cpp b/components/sceneutil/util.cpp index ed891f835e..e7a9cab730 100644 --- a/components/sceneutil/util.cpp +++ b/components/sceneutil/util.cpp @@ -16,28 +16,30 @@ #include #include #include +#include namespace SceneUtil { namespace { - std::array generateGlowTextureNames() + std::array generateGlowTextureNames() { - std::array result; + constexpr VFS::Path::NormalizedView prefix("textures/magicitem"); + std::array result; for (std::size_t i = 0; i < result.size(); ++i) { std::stringstream stream; - stream << "textures/magicitem/caust"; + stream << "caust"; stream << std::setw(2); stream << std::setfill('0'); stream << i; stream << ".dds"; - result[i] = std::move(stream).str(); + result[i] = prefix / VFS::Path::Normalized(std::move(stream).str()); } return result; } - const std::array glowTextureNames = generateGlowTextureNames(); + const std::array glowTextureNames = generateGlowTextureNames(); struct FindLowestUnusedTexUnitVisitor : public osg::NodeVisitor { @@ -219,9 +221,9 @@ namespace SceneUtil const osg::Vec4f& glowColor, float glowDuration) { std::vector> textures; - for (const std::string& name : glowTextureNames) + for (const VFS::Path::Normalized& name : glowTextureNames) { - osg::ref_ptr image = resourceSystem->getImageManager()->getImage(VFS::Path::toNormalized(name)); + osg::ref_ptr image = resourceSystem->getImageManager()->getImage(name); osg::ref_ptr tex(new osg::Texture2D(image)); tex->setWrap(osg::Texture::WRAP_S, osg::Texture2D::REPEAT); tex->setWrap(osg::Texture::WRAP_T, osg::Texture2D::REPEAT);