diff --git a/apps/bulletobjecttool/main.cpp b/apps/bulletobjecttool/main.cpp index b27c8135d6..4dbdb56350 100644 --- a/apps/bulletobjecttool/main.cpp +++ b/apps/bulletobjecttool/main.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -173,7 +174,8 @@ namespace constexpr double expiryDelay = 0; Resource::ImageManager imageManager(&vfs, expiryDelay); Resource::NifFileManager nifFileManager(&vfs, &encoder.getStatelessEncoder()); - Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, expiryDelay); + Resource::BgsmFileManager bgsmFileManager(&vfs, expiryDelay); + Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, &bgsmFileManager, expiryDelay); Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay); Resource::forEachBulletObject( diff --git a/apps/navmeshtool/main.cpp b/apps/navmeshtool/main.cpp index 94ab7ef082..d75a1af5e2 100644 --- a/apps/navmeshtool/main.cpp +++ b/apps/navmeshtool/main.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -220,7 +221,8 @@ namespace NavMeshTool Resource::ImageManager imageManager(&vfs, expiryDelay); Resource::NifFileManager nifFileManager(&vfs, &encoder.getStatelessEncoder()); - Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, expiryDelay); + Resource::BgsmFileManager bgsmFileManager(&vfs, expiryDelay); + Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, &bgsmFileManager, expiryDelay); Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay); DetourNavigator::RecastGlobalAllocator::init(); DetourNavigator::Settings navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager(); diff --git a/apps/openmw_test_suite/nifosg/testnifloader.cpp b/apps/openmw_test_suite/nifosg/testnifloader.cpp index f05d651301..cdab51e6c2 100644 --- a/apps/openmw_test_suite/nifosg/testnifloader.cpp +++ b/apps/openmw_test_suite/nifosg/testnifloader.cpp @@ -70,7 +70,7 @@ namespace init(node); Nif::NIFFile file("test.nif"); file.mRoots.push_back(&node); - auto result = Loader::load(file, &mImageManager); + auto result = Loader::load(file, &mImageManager, nullptr); EXPECT_EQ(serialize(*result), R"( osg::Group { UniqueID 1 @@ -259,7 +259,7 @@ osg::Group { node.mProperties.push_back(Nif::RecordPtrT(&property)); Nif::NIFFile file("test.nif"); file.mRoots.push_back(&node); - auto result = Loader::load(file, &mImageManager); + auto result = Loader::load(file, &mImageManager, nullptr); EXPECT_EQ(serialize(*result), formatOsgNodeForBSShaderProperty(GetParam().mExpectedShaderPrefix)); } @@ -289,7 +289,7 @@ osg::Group { node.mProperties.push_back(Nif::RecordPtrT(&property)); Nif::NIFFile file("test.nif"); file.mRoots.push_back(&node); - auto result = Loader::load(file, &mImageManager); + auto result = Loader::load(file, &mImageManager, nullptr); EXPECT_EQ(serialize(*result), formatOsgNodeForBSLightingShaderProperty(GetParam().mExpectedShaderPrefix)); } diff --git a/components/misc/resourcehelpers.cpp b/components/misc/resourcehelpers.cpp index 1d5b57bfd9..5c3f87b3e7 100644 --- a/components/misc/resourcehelpers.cpp +++ b/components/misc/resourcehelpers.cpp @@ -173,6 +173,11 @@ std::string Misc::ResourceHelpers::correctActorModelPath(const std::string& resP return mdlname; } +std::string Misc::ResourceHelpers::correctMaterialPath(std::string_view resPath, const VFS::Manager* vfs) +{ + return correctResourcePath({ { "materials" } }, resPath, vfs); +} + std::string Misc::ResourceHelpers::correctMeshPath(std::string_view resPath) { std::string res = "meshes\\"; diff --git a/components/misc/resourcehelpers.hpp b/components/misc/resourcehelpers.hpp index cda99d928d..a2e05610a6 100644 --- a/components/misc/resourcehelpers.hpp +++ b/components/misc/resourcehelpers.hpp @@ -34,6 +34,7 @@ namespace Misc /// 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". :( std::string correctActorModelPath(const std::string& resPath, const VFS::Manager* vfs); + std::string correctMaterialPath(std::string_view resPath, const VFS::Manager* vfs); // Adds "meshes\\". std::string correctMeshPath(std::string_view resPath); diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 8d46b0f751..0150c2dc90 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include // particle @@ -42,6 +43,7 @@ #include #include +#include #include #include #include @@ -238,15 +240,17 @@ namespace NifOsg { public: /// @param filename used for warning messages. - LoaderImpl(const std::filesystem::path& filename, unsigned int ver, unsigned int userver, unsigned int bethver) + LoaderImpl(const std::filesystem::path& filename, unsigned int ver, unsigned int userver, unsigned int bethver, Resource::BgsmFileManager* materialMgr) : mFilename(filename) , mVersion(ver) , mUserVersion(userver) , mBethVersion(bethver) + , mMaterialMgr(materialMgr) { } std::filesystem::path mFilename; unsigned int mVersion, mUserVersion, mBethVersion; + Resource::BgsmFileManager* mMaterialMgr; size_t mFirstRootTextureIndex{ ~0u }; bool mFoundFirstRootTexturingProperty = false; @@ -2155,6 +2159,98 @@ namespace NifOsg handleTextureControllers(texprop, composite, imageManager, stateset, animflags); } + void handleShaderMaterial(const std::string& path, osg::StateSet* stateset, Resource::ImageManager* imageManager, + std::vector& boundTextures) + { + if (!mMaterialMgr) + return; + + Bgsm::MaterialFilePtr material = mMaterialMgr->get(VFS::Path::Normalized(path)); + if (!material) + return; + + if (material->mShaderType == Bgsm::ShaderType::Lighting) + { + const Bgsm::BGSMFile* bgsm = static_cast(material.get()); + + if (!boundTextures.empty()) + { + for (unsigned int i = 0; i < boundTextures.size(); ++i) + stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF); + boundTextures.clear(); + } + + const unsigned int uvSet = 0; + if (!bgsm->mDiffuseMap.empty()) + { + std::string filename + = Misc::ResourceHelpers::correctTexturePath(bgsm->mDiffuseMap, imageManager->getVFS()); + osg::ref_ptr image = imageManager->getImage(filename); + osg::ref_ptr texture2d = new osg::Texture2D(image); + if (image) + texture2d->setTextureSize(image->s(), image->t()); + handleTextureWrapping(texture2d, (bgsm->mClamp >> 1) & 0x1, bgsm->mClamp & 0x1); + unsigned int texUnit = boundTextures.size(); + stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); + texture2d->setName("diffuseMap"); + boundTextures.emplace_back(uvSet); + } + + if (!bgsm->mNormalMap.empty()) + { + std::string filename + = Misc::ResourceHelpers::correctTexturePath(bgsm->mNormalMap, imageManager->getVFS()); + osg::ref_ptr image = imageManager->getImage(filename); + osg::ref_ptr texture2d = new osg::Texture2D(image); + if (image) + texture2d->setTextureSize(image->s(), image->t()); + handleTextureWrapping(texture2d, (bgsm->mClamp >> 1) & 0x1, bgsm->mClamp & 0x1); + unsigned int texUnit = boundTextures.size(); + stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); + texture2d->setName("normalMap"); + boundTextures.emplace_back(uvSet); + } + + if (bgsm->mTwoSided) + stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + if (bgsm->mTree) + stateset->addUniform(new osg::Uniform("useTreeAnim", true)); + + handleDepthFlags(stateset, bgsm->mDepthTest, bgsm->mDepthWrite); + } + else + { + const Bgsm::BGEMFile* bgem = static_cast(material.get()); + if (!bgem->mBaseMap.empty()) + { + if (!boundTextures.empty()) + { + for (unsigned int i = 0; i < boundTextures.size(); ++i) + stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF); + boundTextures.clear(); + } + std::string filename = Misc::ResourceHelpers::correctTexturePath( + bgem->mBaseMap, imageManager->getVFS()); + osg::ref_ptr image = imageManager->getImage(filename); + osg::ref_ptr texture2d = new osg::Texture2D(image); + texture2d->setName("diffuseMap"); + if (image) + texture2d->setTextureSize(image->s(), image->t()); + handleTextureWrapping(texture2d, (bgem->mClamp >> 1) & 0x1, bgem->mClamp & 0x1); + const unsigned int texUnit = 0; + const unsigned int uvSet = 0; + stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); + boundTextures.push_back(uvSet); + } + + bool useFalloff = bgem->mFalloff; + stateset->addUniform(new osg::Uniform("useFalloff", useFalloff)); + if (useFalloff) + stateset->addUniform(new osg::Uniform("falloffParams", bgem->mFalloffParams)); + handleDepthFlags(stateset, bgem->mDepthTest, bgem->mDepthWrite); + } + } + void handleTextureSet(const Nif::BSShaderTextureSet* textureSet, unsigned int clamp, const std::string& nodeName, osg::StateSet* stateset, Resource::ImageManager* imageManager, std::vector& boundTextures) @@ -2421,6 +2517,12 @@ namespace NifOsg node->setUserValue("shaderPrefix", std::string(getBSLightingShaderPrefix(texprop->mType))); node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); + std::string normalizedName = Misc::ResourceHelpers::correctMaterialPath(texprop->mName, mMaterialMgr->getVFS()); + if (normalizedName.ends_with(".bgsm")) + { + handleShaderMaterial(normalizedName, stateset, imageManager, boundTextures); + break; + } if (!texprop->mTextureSet.empty()) handleTextureSet(texprop->mTextureSet.getPtr(), texprop->mClamp, node->getName(), stateset, imageManager, boundTextures); @@ -2442,6 +2544,12 @@ namespace NifOsg node->setUserValue("shaderPrefix", std::string("bs/nolighting")); node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); + std::string normalizedName = Misc::ResourceHelpers::correctMaterialPath(texprop->mName, mMaterialMgr->getVFS()); + if (normalizedName.ends_with(".bgem")) + { + handleShaderMaterial(normalizedName, stateset, imageManager, boundTextures); + break; + } if (!texprop->mSourceTexture.empty()) { if (!boundTextures.empty()) @@ -2860,15 +2968,15 @@ namespace NifOsg } }; - osg::ref_ptr Loader::load(Nif::FileView file, Resource::ImageManager* imageManager) + osg::ref_ptr Loader::load(Nif::FileView file, Resource::ImageManager* imageManager, Resource::BgsmFileManager* materialMgr) { - LoaderImpl impl(file.getFilename(), file.getVersion(), file.getUserVersion(), file.getBethVersion()); + LoaderImpl impl(file.getFilename(), file.getVersion(), file.getUserVersion(), file.getBethVersion(), materialMgr); return impl.load(file, imageManager); } void Loader::loadKf(Nif::FileView kf, SceneUtil::KeyframeHolder& target) { - LoaderImpl impl(kf.getFilename(), kf.getVersion(), kf.getUserVersion(), kf.getBethVersion()); + LoaderImpl impl(kf.getFilename(), kf.getVersion(), kf.getUserVersion(), kf.getBethVersion(), nullptr); impl.loadKf(kf, target); } diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index 21e0ae097c..b016248f07 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -18,6 +18,7 @@ namespace osg namespace Resource { class ImageManager; + class BgsmFileManager; } namespace NifOsg @@ -30,7 +31,7 @@ namespace NifOsg public: /// Create a scene graph for the given NIF. Auto-detects when skinning is used and wraps the graph in a Skeleton /// if so. - static osg::ref_ptr load(Nif::FileView file, Resource::ImageManager* imageManager); + static osg::ref_ptr load(Nif::FileView file, Resource::ImageManager* imageManager, Resource::BgsmFileManager* materialManager); /// Load keyframe controllers from the given kf file. static void loadKf(Nif::FileView kf, SceneUtil::KeyframeHolder& target); diff --git a/components/resource/resourcesystem.cpp b/components/resource/resourcesystem.cpp index 65a83a60ab..33bba791a8 100644 --- a/components/resource/resourcesystem.cpp +++ b/components/resource/resourcesystem.cpp @@ -2,6 +2,7 @@ #include +#include "bgsmfilemanager.hpp" #include "imagemanager.hpp" #include "keyframemanager.hpp" #include "niffilemanager.hpp" @@ -15,11 +16,13 @@ namespace Resource : mVFS(vfs) { mNifFileManager = std::make_unique(vfs, encoder); + mBgsmFileManager = std::make_unique(vfs, expiryDelay); mImageManager = std::make_unique(vfs, expiryDelay); - mSceneManager = std::make_unique(vfs, mImageManager.get(), mNifFileManager.get(), expiryDelay); + mSceneManager = std::make_unique(vfs, mImageManager.get(), mNifFileManager.get(), mBgsmFileManager.get(), expiryDelay); mKeyframeManager = std::make_unique(vfs, mSceneManager.get(), expiryDelay, encoder); addResourceManager(mNifFileManager.get()); + addResourceManager(mBgsmFileManager.get()); addResourceManager(mKeyframeManager.get()); // note, scene references images so add images afterwards for correct implementation of updateCache() addResourceManager(mSceneManager.get()); @@ -43,6 +46,11 @@ namespace Resource return mImageManager.get(); } + BgsmFileManager* ResourceSystem::getBgsmFileManager() + { + return mBgsmFileManager.get(); + } + NifFileManager* ResourceSystem::getNifFileManager() { return mNifFileManager.get(); diff --git a/components/resource/resourcesystem.hpp b/components/resource/resourcesystem.hpp index f7f09b9277..5609176a89 100644 --- a/components/resource/resourcesystem.hpp +++ b/components/resource/resourcesystem.hpp @@ -25,6 +25,7 @@ namespace Resource class SceneManager; class ImageManager; + class BgsmFileManager; class NifFileManager; class KeyframeManager; class BaseResourceManager; @@ -41,6 +42,7 @@ namespace Resource SceneManager* getSceneManager(); ImageManager* getImageManager(); + BgsmFileManager* getBgsmFileManager(); NifFileManager* getNifFileManager(); KeyframeManager* getKeyframeManager(); @@ -74,6 +76,7 @@ namespace Resource private: std::unique_ptr mSceneManager; std::unique_ptr mImageManager; + std::unique_ptr mBgsmFileManager; std::unique_ptr mNifFileManager; std::unique_ptr mKeyframeManager; diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index ab3f92f10d..daeafeaf1d 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -55,6 +55,7 @@ #include #include +#include "bgsmfilemanager.hpp" #include "errormarker.hpp" #include "imagemanager.hpp" #include "niffilemanager.hpp" @@ -409,7 +410,7 @@ namespace Resource }; SceneManager::SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, - Resource::NifFileManager* nifFileManager, double expiryDelay) + Resource::NifFileManager* nifFileManager, Resource::BgsmFileManager* bgsmFileManager, double expiryDelay) : ResourceManager(vfs, expiryDelay) , mShaderManager(new Shader::ShaderManager) , mForceShaders(false) @@ -424,6 +425,7 @@ namespace Resource , mSharedStateManager(new SharedStateManager) , mImageManager(imageManager) , mNifFileManager(nifFileManager) + , mBgsmFileManager(bgsmFileManager) , mMinFilter(osg::Texture::LINEAR_MIPMAP_LINEAR) , mMagFilter(osg::Texture::LINEAR) , mMaxAnisotropy(1) @@ -795,11 +797,11 @@ namespace Resource } osg::ref_ptr load(VFS::Path::NormalizedView normalizedFilename, const VFS::Manager* vfs, - Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager) + Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager, Resource::BgsmFileManager* materialMgr) { const std::string_view ext = Misc::getFileExtension(normalizedFilename.value()); if (ext == "nif") - return NifOsg::Loader::load(*nifFileManager->get(normalizedFilename), imageManager); + return NifOsg::Loader::load(*nifFileManager->get(normalizedFilename), imageManager, materialMgr); else if (ext == "spt") { Log(Debug::Warning) << "Ignoring SpeedTree data file " << normalizedFilename; @@ -921,7 +923,7 @@ namespace Resource { path.changeExtension(meshType); if (mVFS->exists(path)) - return load(path, mVFS, mImageManager, mNifFileManager); + return load(path, mVFS, mImageManager, mNifFileManager, mBgsmFileManager); } } catch (const std::exception& e) @@ -953,7 +955,7 @@ namespace Resource osg::ref_ptr loaded; try { - loaded = load(normalized, mVFS, mImageManager, mNifFileManager); + loaded = load(normalized, mVFS, mImageManager, mNifFileManager, mBgsmFileManager); SceneUtil::ProcessExtraDataVisitor extraDataVisitor(this); loaded->accept(extraDataVisitor); diff --git a/components/resource/scenemanager.hpp b/components/resource/scenemanager.hpp index 3ad8a24892..31ad51694c 100644 --- a/components/resource/scenemanager.hpp +++ b/components/resource/scenemanager.hpp @@ -32,6 +32,7 @@ namespace Resource { class ImageManager; class NifFileManager; + class BgsmFileManager; class SharedStateManager; } @@ -90,7 +91,7 @@ namespace Resource { public: explicit SceneManager(const VFS::Manager* vfs, Resource::ImageManager* imageManager, - Resource::NifFileManager* nifFileManager, double expiryDelay); + Resource::NifFileManager* nifFileManager, Resource::BgsmFileManager* bgsmFileManager, double expiryDelay); ~SceneManager(); Shader::ShaderManager& getShaderManager(); @@ -259,6 +260,7 @@ namespace Resource Resource::ImageManager* mImageManager; Resource::NifFileManager* mNifFileManager; + Resource::BgsmFileManager* mBgsmFileManager; osg::Texture::FilterMode mMinFilter; osg::Texture::FilterMode mMagFilter;