From aea7b10986343bca77ae74c9f2892c9cf7161dc6 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Wed, 17 Apr 2024 22:53:55 +0300 Subject: [PATCH 01/24] Add dummy BGSM/BGEM file reader --- components/CMakeLists.txt | 4 + components/bgsm/file.cpp | 20 +++++ components/bgsm/file.hpp | 164 +++++++++++++++++++++++++++++++++++++ components/bgsm/reader.cpp | 35 ++++++++ components/bgsm/reader.hpp | 25 ++++++ components/bgsm/stream.cpp | 110 +++++++++++++++++++++++++ components/bgsm/stream.hpp | 143 ++++++++++++++++++++++++++++++++ 7 files changed, 501 insertions(+) create mode 100644 components/bgsm/file.cpp create mode 100644 components/bgsm/file.hpp create mode 100644 components/bgsm/reader.cpp create mode 100644 components/bgsm/reader.hpp create mode 100644 components/bgsm/stream.cpp create mode 100644 components/bgsm/stream.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 68411be2fc..084deaea58 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -107,6 +107,10 @@ add_component_dir (settings windowmode ) +add_component_dir (bgsm + reader stream file + ) + add_component_dir (bsa bsa_file compressedbsafile ba2gnrlfile ba2dx10file ba2file memorystream ) diff --git a/components/bgsm/file.cpp b/components/bgsm/file.cpp new file mode 100644 index 0000000000..870d9e4067 --- /dev/null +++ b/components/bgsm/file.cpp @@ -0,0 +1,20 @@ +#include "file.hpp" + +#include "stream.hpp" + +namespace Bgsm +{ + void MaterialFile::read(BGSMStream& stream) + { + } + + void BGSMFile::read(BGSMStream& stream) + { + MaterialFile::read(stream); + } + + void BGEMFile::read(BGSMStream& stream) + { + MaterialFile::read(stream); + } +} diff --git a/components/bgsm/file.hpp b/components/bgsm/file.hpp new file mode 100644 index 0000000000..117b135e4f --- /dev/null +++ b/components/bgsm/file.hpp @@ -0,0 +1,164 @@ +#ifndef OPENMW_COMPONENTS_BGSM_FILE_HPP +#define OPENMW_COMPONENTS_BGSM_FILE_HPP + +#include +#include +#include +#include + +#include +#include +#include + +namespace Bgsm +{ + class BGSMStream; + + enum class ShaderType + { + Lighting, + Effect, + }; + + struct MaterialFile + { + ShaderType mShaderType; + std::uint32_t mVersion; + std::uint32_t mClamp; + osg::Vec2f mUVOffset, mUVScale; + float mTransparency; + std::uint8_t mSourceBlendMode; + std::uint32_t mDestinationBlendMode; + std::uint32_t mAlphaTestMode; + std::uint8_t mAlphaTestRef; + bool mAlphaTest; + bool mDepthWrite, mDepthTest; + bool mSSR; + bool mWetnessControlSSR; + bool mDecal; + bool mTwoSided; + bool mDecalNoFade; + bool mNonOccluder; + bool mRefraction; + bool mRefractionFalloff; + float mRefractionPower; + bool mEnvMap; + float mEnvMapMaskScale; + bool mDepthBias; + bool mGrayscaleToPaletteColor; + std::uint8_t mMaskWrites; + + MaterialFile() = default; + virtual void read(BGSMStream& stream); + virtual ~MaterialFile() {} + }; + + struct BGSMFile : MaterialFile + { + std::string mDiffuseMap; + std::string mNormalMap; + std::string mSmoothSpecMap; + std::string mGreyscaleMap; + std::string mGlowMap; + std::string mWrinkleMap; + std::string mSpecularMap; + std::string mLightingMap; + std::string mFlowMap; + std::string mDistanceFieldAlphaMap; + std::string mEnvMap; + std::string mInnerLayerMap; + std::string mDisplacementMap; + bool mEnableEditorAlphaRef; + bool mTranslucency; + bool mTranslucencyThickObject; + bool mTranslucencyMixAlbedoWithSubsurfaceColor; + osg::Vec4f mTranslucencySubsurfaceColor; + float mTranslucencyTransmissiveScale; + float mTranslucencyTurbulence; + bool mRimLighting; + float mRimPower; + float mBackLightPower; + bool mSursurfaceLighting; + float mSubsurfaceLightingRolloff; + bool mSpecularEnabled; + osg::Vec4f mSpecularColor; + float mSpecularMult; + float mSmoothness; + float mFresnelPower; + float mWetnessControlSpecScale; + float mWetnessControlSpecPowerScale; + float mWetnessControlSpecMinvar; + float mWetnessControlEnvMapScale; + float mWetnessControlFresnelPower; + float mWetnessControlMetalness; + bool mPBR; + bool mCustomPorosity; + float mPorosityValue; + std::string mRootMaterialPath; + bool mAnisoLighting; + bool mEmitEnabled; + osg::Vec4f mEmittanceColor; + float mEmittanceMult; + bool mModelSpaceNormals; + bool mExternalEmittance; + float mLumEmittance; + bool mUseAdaptiveEmissive; + osg::Vec3f mAdaptiveEmissiveExposureParams; + bool mBackLighting; + bool mReceiveShadows; + bool mHideSecret; + bool mCastShadows; + bool mDissolveFade; + bool mAssumeShadowmask; + bool mHasGlowMap; + bool mEnvMapWindow; + bool mEnvMapEye; + bool mHair; + osg::Vec4f mHairTintColor; + bool mTree; + bool mFacegen; + bool mSkinTint; + bool mTessellate; + osg::Vec2f mDisplacementMapParams; + osg::Vec3f mTesselationParams; + float mGrayscaleToPaletteScale; + bool mSkewSpecularAlpha; + bool mTerrain; + osg::Vec3f mTerrainParams; + + void read(BGSMStream& stream) override; + }; + + struct BGEMFile : MaterialFile + { + std::string mBaseMap; + std::string mGrayscaleMap; + std::string mEnvMap; + std::string mNormalMap; + std::string mEnvMapMask; + std::string mSpecularMap; + std::string mLightingMap; + std::string mGlowMap; + bool mBlood; + bool mEffectLighting; + bool mFalloff; + bool mFalloffColor; + bool mGrayscaleToPaletteAlpha; + bool mSoft; + osg::Vec4f mBaseColor; + float mBaseColorScale; + osg::Vec4f mFalloffParams; + float mLightingInfluence; + std::uint8_t mEnvmapMinLOD; + float mSoftDepth; + osg::Vec4f mEmittanceColor; + osg::Vec3f mAdaptiveEmissiveExposureParams; + bool mHasGlowMap; + bool mEffectPbrSpecular; + + void read(BGSMStream& stream) override; + }; + + using MaterialFilePtr = std::shared_ptr; +} +#endif diff --git a/components/bgsm/reader.cpp b/components/bgsm/reader.cpp new file mode 100644 index 0000000000..c89d872bd7 --- /dev/null +++ b/components/bgsm/reader.cpp @@ -0,0 +1,35 @@ +#include "reader.hpp" + +#include +#include +#include + +#include "file.hpp" +#include "stream.hpp" + +namespace Bgsm +{ + void Reader::parse(Files::IStreamPtr&& inputStream) + { + BGSMStream stream(*this, std::move(inputStream)); + + std::array signature; + stream.readArray(signature); + std::string shaderType(signature.data(), 4); + if (shaderType == "BGEM") + { + mFile = std::make_unique(); + mFile->mShaderType = Bgsm::ShaderType::Effect; + } + else if (shaderType == "BGSM") + { + mFile = std::make_unique(); + mFile->mShaderType = Bgsm::ShaderType::Lighting; + } + else + throw std::runtime_error("Invalid material file"); + + mFile->read(stream); + } + +} diff --git a/components/bgsm/reader.hpp b/components/bgsm/reader.hpp new file mode 100644 index 0000000000..5ac67c0467 --- /dev/null +++ b/components/bgsm/reader.hpp @@ -0,0 +1,25 @@ +#ifndef OPENMW_COMPONENTS_BGSM_READER_HPP +#define OPENMW_COMPONENTS_BGSM_READER_HPP + +#include +#include +#include +#include + +#include + +#include "file.hpp" + +namespace Bgsm +{ + class Reader + { + std::unique_ptr mFile; + + public: + void parse(Files::IStreamPtr&& stream); + + std::uint32_t getVersion() const { return mFile->mVersion; } + }; +} +#endif diff --git a/components/bgsm/stream.cpp b/components/bgsm/stream.cpp new file mode 100644 index 0000000000..d8434fcb8a --- /dev/null +++ b/components/bgsm/stream.cpp @@ -0,0 +1,110 @@ +#include "stream.hpp" + +#include + +#include "reader.hpp" + +namespace +{ + + // Read a range of elements into a dynamic buffer per-element + // This one should be used if the type cannot be read contiguously + // (e.g. quaternions) + template + void readRange(Bgsm::BGSMStream& stream, T* dest, size_t size) + { + for (T& value : std::span(dest, size)) + stream.read(value); + } + + // Read a range of elements into a dynamic buffer + // This one should be used if the type can be read contiguously as an array of a different type + // (e.g. osg::VecXf can be read as a float array of X elements) + template + void readAlignedRange(Files::IStreamPtr& stream, T* dest, size_t size) + { + static_assert(std::is_standard_layout_v); + static_assert(std::alignment_of_v == std::alignment_of_v); + static_assert(sizeof(T) == sizeof(elementType) * numElements); + Bgsm::readDynamicBufferOfType(stream, reinterpret_cast(dest), size * numElements); + } + +} + +namespace Bgsm +{ + + std::uint32_t BGSMStream::getVersion() const + { + return mReader.getVersion(); + } + + std::string BGSMStream::getSizedString(size_t length) + { + std::string str(length, '\0'); + mStream->read(str.data(), length); + if (mStream->bad()) + throw std::runtime_error("Failed to read sized string of " + std::to_string(length) + " chars"); + size_t end = str.find('\0'); + if (end != std::string::npos) + str.erase(end); + return str; + } + + void BGSMStream::getSizedStrings(std::vector& vec, size_t size) + { + vec.resize(size); + for (size_t i = 0; i < vec.size(); i++) + vec[i] = getSizedString(); + } + + template <> + void BGSMStream::read(osg::Vec2f& vec) + { + readBufferOfType(mStream, vec._v); + } + + template <> + void BGSMStream::read(osg::Vec3f& vec) + { + readBufferOfType(mStream, vec._v); + } + + template <> + void BGSMStream::read(osg::Vec4f& vec) + { + readBufferOfType(mStream, vec._v); + } + + template <> + void BGSMStream::read(std::string& str) + { + str = getSizedString(); + } + + template <> + void BGSMStream::read(osg::Vec2f* dest, size_t size) + { + readAlignedRange(mStream, dest, size); + } + + template <> + void BGSMStream::read(osg::Vec3f* dest, size_t size) + { + readAlignedRange(mStream, dest, size); + } + + template <> + void BGSMStream::read(osg::Vec4f* dest, size_t size) + { + readAlignedRange(mStream, dest, size); + } + + template <> + void BGSMStream::read(std::string* dest, size_t size) + { + for (std::string& value : std::span(dest, size)) + value = getSizedString(); + } + +} diff --git a/components/bgsm/stream.hpp b/components/bgsm/stream.hpp new file mode 100644 index 0000000000..8b0e1efdbe --- /dev/null +++ b/components/bgsm/stream.hpp @@ -0,0 +1,143 @@ +#ifndef OPENMW_COMPONENTS_BGSM_STREAM_HPP +#define OPENMW_COMPONENTS_BGSM_STREAM_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace Bgsm +{ + class Reader; + + template + inline void readBufferOfType(Files::IStreamPtr& pIStream, T* dest) + { + static_assert(std::is_arithmetic_v, "Buffer element type is not arithmetic"); + pIStream->read((char*)dest, numInstances * sizeof(T)); + if (pIStream->bad()) + throw std::runtime_error("Failed to read typed (" + std::string(typeid(T).name()) + ") buffer of " + + std::to_string(numInstances) + " instances"); + if constexpr (Misc::IS_BIG_ENDIAN) + for (std::size_t i = 0; i < numInstances; i++) + Misc::swapEndiannessInplace(dest[i]); + } + + template + inline void readBufferOfType(Files::IStreamPtr& pIStream, T (&dest)[numInstances]) + { + readBufferOfType(pIStream, static_cast(dest)); + } + + template + inline void readDynamicBufferOfType(Files::IStreamPtr& pIStream, T* dest, std::size_t numInstances) + { + static_assert(std::is_arithmetic_v, "Buffer element type is not arithmetic"); + pIStream->read((char*)dest, numInstances * sizeof(T)); + if (pIStream->bad()) + throw std::runtime_error("Failed to read typed (" + std::string(typeid(T).name()) + ") dynamic buffer of " + + std::to_string(numInstances) + " instances"); + if constexpr (Misc::IS_BIG_ENDIAN) + for (std::size_t i = 0; i < numInstances; i++) + Misc::swapEndiannessInplace(dest[i]); + } + + class BGSMStream + { + const Reader& mReader; + Files::IStreamPtr mStream; + + public: + explicit BGSMStream( + const Reader& reader, Files::IStreamPtr&& stream) + : mReader(reader) + , mStream(std::move(stream)) + { + } + + const Reader& getFile() const { return mReader; } + + std::uint32_t getVersion() const; + + void skip(size_t size) { mStream->ignore(size); } + + /// Read into a single instance of type + template + void read(T& data) + { + readBufferOfType<1>(mStream, &data); + } + + /// Read multiple instances of type into an array + template + void readArray(std::array& arr) + { + readBufferOfType(mStream, arr.data()); + } + + /// Read instances of type into a dynamic buffer + template + void read(T* dest, size_t size) + { + readDynamicBufferOfType(mStream, dest, size); + } + + /// Read multiple instances of type into a vector + template + void readVector(std::vector& vec, size_t size) + { + if (size == 0) + return; + vec.resize(size); + read(vec.data(), size); + } + + /// Extract an instance of type + template + T get() + { + T data; + read(data); + return data; + } + + /// Read a string of the given length + std::string getSizedString(size_t length); + + /// Read a string of the length specified in the file + std::string getSizedString() { return getSizedString(get()); } + + /// Read a list of strings + void getSizedStrings(std::vector& vec, size_t size); + }; + + template <> + void BGSMStream::read(osg::Vec2f& vec); + template <> + void BGSMStream::read(osg::Vec3f& vec); + template <> + void BGSMStream::read(osg::Vec4f& vec); + template <> + void BGSMStream::read(std::string& str); + + template <> + void BGSMStream::read(osg::Vec2f* dest, size_t size); + template <> + void BGSMStream::read(osg::Vec3f* dest, size_t size); + template <> + void BGSMStream::read(osg::Vec4f* dest, size_t size); + template <> + void BGSMStream::read(std::string* dest, size_t size); +} + +#endif From 4a03555d53d6794d077470e33a65367ca8f61dba Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 18 Apr 2024 01:00:49 +0300 Subject: [PATCH 02/24] Add BGEM/BGSM file support to niftest --- apps/niftest/niftest.cpp | 50 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index b37d85d739..3cd4382121 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -36,6 +37,13 @@ bool isNIF(const std::filesystem::path& filename) { return hasExtension(filename, ".nif") || hasExtension(filename, ".kf"); } + +/// Check if the file is a material file. +bool isMaterial(const std::filesystem::path& filename) +{ + return hasExtension(filename, ".bgem") || hasExtension(filename, ".bgsm"); +} + /// See if the file has the "bsa" extension. bool isBSA(const std::filesystem::path& filename) { @@ -81,6 +89,36 @@ void readNIF( } } + +void readMaterial( + const std::filesystem::path& source, const std::filesystem::path& path, const VFS::Manager* vfs, bool quiet) +{ + const std::string pathStr = Files::pathToUnicodeString(path); + if (!quiet) + { + if (hasExtension(path, ".bgem")) + std::cout << "Reading BGEM file '" << pathStr << "'"; + else + std::cout << "Reading BGSM file '" << pathStr << "'"; + if (!source.empty()) + std::cout << " from '" << Files::pathToUnicodeString(isBSA(source) ? source.filename() : source) << "'"; + std::cout << std::endl; + } + const std::filesystem::path fullPath = !source.empty() ? source / path : path; + try + { + Bgsm::Reader reader; + if (vfs != nullptr) + reader.parse(vfs->get(pathStr)); + else + reader.parse(Files::openConstrainedFileStream(fullPath)); + } + catch (std::exception& e) + { + std::cerr << "Failed to read '" << pathStr << "':" << std::endl << e.what() << std::endl; + } +} + /// Check all the nif files in a given VFS::Archive /// \note Can not read a bsa file inside of a bsa file. void readVFS(std::unique_ptr&& archive, const std::filesystem::path& archivePath, bool quiet) @@ -101,6 +139,10 @@ void readVFS(std::unique_ptr&& archive, const std::filesystem::pat { readNIF(archivePath, name.value(), &vfs, quiet); } + else if (isMaterial(name.value())) + { + readMaterial(archivePath, name.value(), &vfs, quiet); + } } if (!archivePath.empty() && !isBSA(archivePath)) @@ -129,10 +171,10 @@ void readVFS(std::unique_ptr&& archive, const std::filesystem::pat bool parseOptions(int argc, char** argv, Files::PathContainer& files, Files::PathContainer& archives, bool& writeDebugLog, bool& quiet) { - bpo::options_description desc(R"(Ensure that OpenMW can use the provided NIF, KF and BSA/BA2 files + bpo::options_description desc(R"(Ensure that OpenMW can use the provided NIF, KF, BGEM/BGSM and BSA/BA2 files Usages: - niftest + niftest Scan the file or directories for NIF errors. Allowed options)"); @@ -225,6 +267,10 @@ int main(int argc, char** argv) { readNIF({}, path, vfs.get(), quiet); } + else if (isMaterial(path)) + { + readMaterial({}, path, vfs.get(), quiet); + } else if (auto archive = makeArchive(path)) { readVFS(std::move(archive), path, quiet); From 124df1be6115a651308c6f310e913aa53c552302 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 18 Apr 2024 01:51:31 +0300 Subject: [PATCH 03/24] Parse shared part of material files --- components/bgsm/file.cpp | 34 ++++++++++++++++++++++++++++++++++ components/bgsm/file.hpp | 6 +++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/components/bgsm/file.cpp b/components/bgsm/file.cpp index 870d9e4067..1ad1918ccf 100644 --- a/components/bgsm/file.cpp +++ b/components/bgsm/file.cpp @@ -6,6 +6,40 @@ namespace Bgsm { void MaterialFile::read(BGSMStream& stream) { + stream.read(mVersion); + stream.read(mClamp); + stream.read(mUVOffset); + stream.read(mUVScale); + stream.read(mTransparency); + stream.read(mAlphaBlend); + stream.read(mSourceBlendMode); + stream.read(mDestinationBlendMode); + stream.read(mAlphaTestThreshold); + stream.read(mAlphaTest); + stream.read(mDepthWrite); + stream.read(mSSR); + stream.read(mWetnessControlSSR); + stream.read(mDecal); + stream.read(mTwoSided); + stream.read(mDecalNoFade); + stream.read(mNonOccluder); + stream.read(mRefraction); + stream.read(mRefractionFalloff); + stream.read(mRefractionPower); + if (mVersion < 10) + { + stream.read(mEnvMap); + stream.read(mEnvMapMaskScale); + } + else + { + stream.read(mDepthBias); + } + stream.read(mGrayscaleToPaletteColor); + if (mVersion >= 6) + { + stream.read(mMaskWrites); + } } void BGSMFile::read(BGSMStream& stream) diff --git a/components/bgsm/file.hpp b/components/bgsm/file.hpp index 117b135e4f..db0059cd29 100644 --- a/components/bgsm/file.hpp +++ b/components/bgsm/file.hpp @@ -27,10 +27,10 @@ namespace Bgsm std::uint32_t mClamp; osg::Vec2f mUVOffset, mUVScale; float mTransparency; - std::uint8_t mSourceBlendMode; + bool mAlphaBlend; + std::uint32_t mSourceBlendMode; std::uint32_t mDestinationBlendMode; - std::uint32_t mAlphaTestMode; - std::uint8_t mAlphaTestRef; + std::uint8_t mAlphaTestThreshold; bool mAlphaTest; bool mDepthWrite, mDepthTest; bool mSSR; From 015aca2cfd986bc048282828e246e042506ce585 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 18 Apr 2024 02:28:48 +0300 Subject: [PATCH 04/24] Initial BGSM file parsing --- components/bgsm/file.cpp | 124 +++++++++++++++++++++++++++++++++++++++ components/bgsm/file.hpp | 6 +- 2 files changed, 127 insertions(+), 3 deletions(-) diff --git a/components/bgsm/file.cpp b/components/bgsm/file.cpp index 1ad1918ccf..bb627ae34e 100644 --- a/components/bgsm/file.cpp +++ b/components/bgsm/file.cpp @@ -45,6 +45,130 @@ namespace Bgsm void BGSMFile::read(BGSMStream& stream) { MaterialFile::read(stream); + + stream.read(mDiffuseMap); + stream.read(mNormalMap); + stream.read(mSmoothSpecMap); + stream.read(mGreyscaleMap); + if (mVersion >= 3) + { + stream.read(mGlowMap); + stream.read(mWrinkleMap); + stream.read(mSpecularMap); + stream.read(mLightingMap); + stream.read(mFlowMap); + if (mVersion >= 17) + { + stream.read(mDistanceFieldAlphaMap); + } + } + else + { + stream.read(mEnvMap); + stream.read(mGlowMap); + stream.read(mInnerLayerMap); + stream.read(mWrinkleMap); + stream.read(mDisplacementMap); + } + stream.read(mEnableEditorAlphaThreshold); + if (mVersion >= 8) + { + stream.read(mTranslucency); + stream.read(mTranslucencyThickObject); + stream.read(mTranslucencyMixAlbedoWithSubsurfaceColor); + stream.read(mTranslucencySubsurfaceColor); + stream.read(mTranslucencyTransmissiveScale); + stream.read(mTranslucencyTurbulence); + } + else + { + stream.read(mRimLighting); + stream.read(mRimPower); + stream.read(mBackLightPower); + stream.read(mSubsurfaceLighting); + stream.read(mSubsurfaceLightingRolloff); + } + stream.read(mSpecularEnabled); + stream.read(mSpecularColor); + stream.read(mSpecularMult); + stream.read(mSmoothness); + stream.read(mFresnelPower); + stream.read(mWetnessControlSpecScale); + stream.read(mWetnessControlSpecPowerScale); + stream.read(mWetnessControlSpecMinvar); + if (mVersion < 10) + { + stream.read(mWetnessControlEnvMapScale); + } + stream.read(mWetnessControlFresnelPower); + stream.read(mWetnessControlMetalness); + if (mVersion >= 3) + { + stream.read(mPBR); + if (mVersion >= 9) + { + stream.read(mCustomPorosity); + stream.read(mPorosityValue); + } + } + stream.read(mRootMaterialPath); + stream.read(mAnisoLighting); + stream.read(mEmitEnabled); + if (mEmitEnabled) + { + stream.read(mEmittanceColor); + } + stream.read(mEmittanceMult); + stream.read(mModelSpaceNormals); + stream.read(mExternalEmittance); + if (mVersion >= 12) + { + stream.read(mLumEmittance); + if (mVersion >= 13) + { + stream.read(mUseAdaptiveEmissive); + stream.read(mAdaptiveEmissiveExposureParams); + } + } + else if (mVersion < 8) + { + stream.read(mBackLighting); + } + stream.read(mReceiveShadows); + stream.read(mHideSecret); + stream.read(mCastShadows); + stream.read(mDissolveFade); + stream.read(mAssumeShadowmask); + stream.read(mHasGlowMap); + if (mVersion < 7) + { + stream.read(mEnvMapWindow); + stream.read(mEnvMapEye); + } + stream.read(mHair); + stream.read(mHairTintColor); + stream.read(mTree); + stream.read(mFacegen); + stream.read(mSkinTint); + stream.read(mTessellate); + if (mVersion < 3) + { + stream.read(mDisplacementMapParams); + stream.read(mTessellationParams); + } + stream.read(mGrayscaleToPaletteScale); + if (mVersion >= 1) + { + stream.read(mSkewSpecularAlpha); + stream.read(mTerrain); + if (mTerrain) + { + if (mVersion == 3) + stream.skip(4); // Unknown + + stream.read(mTerrainParams); + } + } } void BGEMFile::read(BGSMStream& stream) diff --git a/components/bgsm/file.hpp b/components/bgsm/file.hpp index db0059cd29..57b08f68dc 100644 --- a/components/bgsm/file.hpp +++ b/components/bgsm/file.hpp @@ -68,7 +68,7 @@ namespace Bgsm std::string mEnvMap; std::string mInnerLayerMap; std::string mDisplacementMap; - bool mEnableEditorAlphaRef; + bool mEnableEditorAlphaThreshold; bool mTranslucency; bool mTranslucencyThickObject; bool mTranslucencyMixAlbedoWithSubsurfaceColor; @@ -78,7 +78,7 @@ namespace Bgsm bool mRimLighting; float mRimPower; float mBackLightPower; - bool mSursurfaceLighting; + bool mSubsurfaceLighting; float mSubsurfaceLightingRolloff; bool mSpecularEnabled; osg::Vec4f mSpecularColor; @@ -120,7 +120,7 @@ namespace Bgsm bool mSkinTint; bool mTessellate; osg::Vec2f mDisplacementMapParams; - osg::Vec3f mTesselationParams; + osg::Vec3f mTessellationParams; float mGrayscaleToPaletteScale; bool mSkewSpecularAlpha; bool mTerrain; From 8ef6304dd9ea205cedfff2cc837e9a92efa5f3b6 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 18 Apr 2024 02:33:30 +0300 Subject: [PATCH 05/24] BGSM colors are Vec3 --- components/bgsm/file.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/components/bgsm/file.hpp b/components/bgsm/file.hpp index 57b08f68dc..00e913917d 100644 --- a/components/bgsm/file.hpp +++ b/components/bgsm/file.hpp @@ -72,7 +72,7 @@ namespace Bgsm bool mTranslucency; bool mTranslucencyThickObject; bool mTranslucencyMixAlbedoWithSubsurfaceColor; - osg::Vec4f mTranslucencySubsurfaceColor; + osg::Vec3f mTranslucencySubsurfaceColor; float mTranslucencyTransmissiveScale; float mTranslucencyTurbulence; bool mRimLighting; @@ -81,7 +81,7 @@ namespace Bgsm bool mSubsurfaceLighting; float mSubsurfaceLightingRolloff; bool mSpecularEnabled; - osg::Vec4f mSpecularColor; + osg::Vec3f mSpecularColor; float mSpecularMult; float mSmoothness; float mFresnelPower; @@ -97,7 +97,7 @@ namespace Bgsm std::string mRootMaterialPath; bool mAnisoLighting; bool mEmitEnabled; - osg::Vec4f mEmittanceColor; + osg::Vec3f mEmittanceColor; float mEmittanceMult; bool mModelSpaceNormals; bool mExternalEmittance; @@ -114,7 +114,7 @@ namespace Bgsm bool mEnvMapWindow; bool mEnvMapEye; bool mHair; - osg::Vec4f mHairTintColor; + osg::Vec3f mHairTintColor; bool mTree; bool mFacegen; bool mSkinTint; @@ -145,13 +145,13 @@ namespace Bgsm bool mFalloffColor; bool mGrayscaleToPaletteAlpha; bool mSoft; - osg::Vec4f mBaseColor; + osg::Vec3f mBaseColor; float mBaseColorScale; osg::Vec4f mFalloffParams; float mLightingInfluence; std::uint8_t mEnvmapMinLOD; float mSoftDepth; - osg::Vec4f mEmittanceColor; + osg::Vec3f mEmittanceColor; osg::Vec3f mAdaptiveEmissiveExposureParams; bool mHasGlowMap; bool mEffectPbrSpecular; From f9f8c1e5912bdd3461e267f12cf4fa3a033d3094 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 18 Apr 2024 02:57:32 +0300 Subject: [PATCH 06/24] Fix depth test reading in BGSM --- components/bgsm/file.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/components/bgsm/file.cpp b/components/bgsm/file.cpp index bb627ae34e..63b2df1d12 100644 --- a/components/bgsm/file.cpp +++ b/components/bgsm/file.cpp @@ -17,6 +17,7 @@ namespace Bgsm stream.read(mAlphaTestThreshold); stream.read(mAlphaTest); stream.read(mDepthWrite); + stream.read(mDepthTest); stream.read(mSSR); stream.read(mWetnessControlSSR); stream.read(mDecal); From 484a360792f42b696baaacd4d6711c204b1ace50 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 18 Apr 2024 02:58:19 +0300 Subject: [PATCH 07/24] Add a safety measure for string loading in BGSM --- components/bgsm/stream.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/bgsm/stream.cpp b/components/bgsm/stream.cpp index d8434fcb8a..00cc382d3f 100644 --- a/components/bgsm/stream.cpp +++ b/components/bgsm/stream.cpp @@ -41,6 +41,9 @@ namespace Bgsm std::string BGSMStream::getSizedString(size_t length) { + // Prevent potential memory allocation freezes; strings this long are not expected in BGSM + if (length > 1024) + throw std::runtime_error("Requested string length is too large: " + std::to_string(length)); std::string str(length, '\0'); mStream->read(str.data(), length); if (mStream->bad()) From cb77bcc4c8797a76e33770b53046f8035b2c9d97 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 18 Apr 2024 03:27:42 +0300 Subject: [PATCH 08/24] Initial BGEM file parsing --- components/bgsm/file.cpp | 51 +++++++++++++++++++++++++++++++++++++--- components/bgsm/file.hpp | 8 +++---- 2 files changed, 52 insertions(+), 7 deletions(-) diff --git a/components/bgsm/file.cpp b/components/bgsm/file.cpp index 63b2df1d12..f330d8a84e 100644 --- a/components/bgsm/file.cpp +++ b/components/bgsm/file.cpp @@ -29,7 +29,7 @@ namespace Bgsm stream.read(mRefractionPower); if (mVersion < 10) { - stream.read(mEnvMap); + stream.read(mEnvMapEnabled); stream.read(mEnvMapMaskScale); } else @@ -50,7 +50,7 @@ namespace Bgsm stream.read(mDiffuseMap); stream.read(mNormalMap); stream.read(mSmoothSpecMap); - stream.read(mGreyscaleMap); + stream.read(mGrayscaleMap); if (mVersion >= 3) { stream.read(mGlowMap); @@ -140,7 +140,7 @@ namespace Bgsm stream.read(mCastShadows); stream.read(mDissolveFade); stream.read(mAssumeShadowmask); - stream.read(mHasGlowMap); + stream.read(mGlowMapEnabled); if (mVersion < 7) { stream.read(mEnvMapWindow); @@ -175,5 +175,50 @@ namespace Bgsm void BGEMFile::read(BGSMStream& stream) { MaterialFile::read(stream); + + stream.read(mBaseMap); + stream.read(mGrayscaleMap); + stream.read(mEnvMap); + stream.read(mNormalMap); + stream.read(mEnvMapMask); + if (mVersion >= 10) + { + if (mVersion >= 11) + { + stream.read(mSpecularMap); + stream.read(mLightingMap); + stream.read(mGlowMap); + } + stream.read(mEnvMapEnabled); + stream.read(mEnvMapMaskScale); + } + stream.read(mBlood); + stream.read(mEffectLighting); + stream.read(mFalloff); + stream.read(mFalloffColor); + stream.read(mGrayscaleToPaletteAlpha); + stream.read(mSoft); + stream.read(mBaseColor); + stream.read(mBaseColorScale); + stream.read(mFalloffParams); + stream.read(mLightingInfluence); + stream.read(mEnvmapMinLOD); + stream.read(mSoftDepth); + if (mVersion >= 11) + { + stream.read(mEmittanceColor); + if (mVersion >= 15) + { + stream.read(mAdaptiveEmissiveExposureParams); + if (mVersion >= 16) + { + stream.read(mGlowMapEnabled); + if (mVersion >= 20) + { + stream.read(mEffectPbrSpecular); + } + } + } + } } } diff --git a/components/bgsm/file.hpp b/components/bgsm/file.hpp index 00e913917d..3524297e2d 100644 --- a/components/bgsm/file.hpp +++ b/components/bgsm/file.hpp @@ -42,7 +42,7 @@ namespace Bgsm bool mRefraction; bool mRefractionFalloff; float mRefractionPower; - bool mEnvMap; + bool mEnvMapEnabled; float mEnvMapMaskScale; bool mDepthBias; bool mGrayscaleToPaletteColor; @@ -58,7 +58,7 @@ namespace Bgsm std::string mDiffuseMap; std::string mNormalMap; std::string mSmoothSpecMap; - std::string mGreyscaleMap; + std::string mGrayscaleMap; std::string mGlowMap; std::string mWrinkleMap; std::string mSpecularMap; @@ -110,7 +110,7 @@ namespace Bgsm bool mCastShadows; bool mDissolveFade; bool mAssumeShadowmask; - bool mHasGlowMap; + bool mGlowMapEnabled; bool mEnvMapWindow; bool mEnvMapEye; bool mHair; @@ -153,7 +153,7 @@ namespace Bgsm float mSoftDepth; osg::Vec3f mEmittanceColor; osg::Vec3f mAdaptiveEmissiveExposureParams; - bool mHasGlowMap; + bool mGlowMapEnabled; bool mEffectPbrSpecular; void read(BGSMStream& stream) override; From fe1cb3a5aeb3da82c5fb11478f859d2014a211e7 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 18 Apr 2024 04:00:20 +0300 Subject: [PATCH 09/24] Add a resource manager for BGSM files --- components/CMakeLists.txt | 2 +- components/bgsm/reader.hpp | 2 + components/resource/bgsmfilemanager.cpp | 62 +++++++++++++++++++++++++ components/resource/bgsmfilemanager.hpp | 27 +++++++++++ components/resource/stats.cpp | 1 + 5 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 components/resource/bgsmfilemanager.cpp create mode 100644 components/resource/bgsmfilemanager.hpp diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 084deaea58..df23121c5c 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -129,7 +129,7 @@ add_component_dir (vfs add_component_dir (resource scenemanager keyframemanager imagemanager bulletshapemanager bulletshape niffilemanager objectcache multiobjectcache resourcesystem - resourcemanager stats animation foreachbulletobject errormarker cachestats + resourcemanager stats animation foreachbulletobject errormarker cachestats bgsmfilemanager ) add_component_dir (shader diff --git a/components/bgsm/reader.hpp b/components/bgsm/reader.hpp index 5ac67c0467..2d669900ad 100644 --- a/components/bgsm/reader.hpp +++ b/components/bgsm/reader.hpp @@ -20,6 +20,8 @@ namespace Bgsm void parse(Files::IStreamPtr&& stream); std::uint32_t getVersion() const { return mFile->mVersion; } + + std::unique_ptr& getFile() { return mFile; } }; } #endif diff --git a/components/resource/bgsmfilemanager.cpp b/components/resource/bgsmfilemanager.cpp new file mode 100644 index 0000000000..5155db17ce --- /dev/null +++ b/components/resource/bgsmfilemanager.cpp @@ -0,0 +1,62 @@ +#include "bgsmfilemanager.hpp" + +#include + +#include + +#include +#include + +#include "objectcache.hpp" + +namespace Resource +{ + + class BgsmFileHolder : public osg::Object + { + public: + BgsmFileHolder(const Bgsm::MaterialFilePtr& file) + : mBgsmFile(file) + { + } + BgsmFileHolder(const BgsmFileHolder& copy, const osg::CopyOp& copyop) + : mBgsmFile(copy.mBgsmFile) + { + } + + BgsmFileHolder() = default; + + META_Object(Resource, BgsmFileHolder) + + Bgsm::MaterialFilePtr mBgsmFile; + }; + + BgsmFileManager::BgsmFileManager(const VFS::Manager* vfs, double expiryDelay) + : ResourceManager(vfs, expiryDelay) + { + } + + BgsmFileManager::~BgsmFileManager() = default; + + Bgsm::MaterialFilePtr BgsmFileManager::get(VFS::Path::NormalizedView name) + { + osg::ref_ptr obj = mCache->getRefFromObjectCache(name); + if (obj) + return static_cast(obj.get())->mBgsmFile; + else + { + Bgsm::Reader reader; + reader.parse(mVFS->get(name)); + Bgsm::MaterialFilePtr file = std::move(reader.getFile()); + obj = new BgsmFileHolder(file); + mCache->addEntryToObjectCache(name.value(), obj); + return file; + } + } + + void BgsmFileManager::reportStats(unsigned int frameNumber, osg::Stats* stats) const + { + Resource::reportStats("BSShader Material", frameNumber, mCache->getStats(), *stats); + } + +} diff --git a/components/resource/bgsmfilemanager.hpp b/components/resource/bgsmfilemanager.hpp new file mode 100644 index 0000000000..b7c0d07c5a --- /dev/null +++ b/components/resource/bgsmfilemanager.hpp @@ -0,0 +1,27 @@ +#ifndef OPENMW_COMPONENTS_RESOURCE_BGSMFILEMANAGER_H +#define OPENMW_COMPONENTS_RESOURCE_BGSMFILEMANAGER_H + +#include + +#include "resourcemanager.hpp" + +namespace Resource +{ + + /// @brief Handles caching of material files. + /// @note May be used from any thread. + class BgsmFileManager : public ResourceManager + { + public: + BgsmFileManager(const VFS::Manager* vfs, double expiryDelay); + ~BgsmFileManager(); + + /// Retrieve a material file from the cache or load it from the VFS if not cached yet. + Bgsm::MaterialFilePtr get(VFS::Path::NormalizedView name); + + void reportStats(unsigned int frameNumber, osg::Stats* stats) const override; + }; + +} + +#endif diff --git a/components/resource/stats.cpp b/components/resource/stats.cpp index 9bb90635d1..6730ddb303 100644 --- a/components/resource/stats.cpp +++ b/components/resource/stats.cpp @@ -87,6 +87,7 @@ namespace Resource "Image", "Nif", "Keyframe", + "BSShader Material", "Groundcover Chunk", "Object Chunk", "Terrain Chunk", From 1a961f30210a6947f8ab39cbacfad042e6259ecd Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 18 Apr 2024 05:01:12 +0300 Subject: [PATCH 10/24] Extremely early handling for BGSM/BGEM files --- apps/bulletobjecttool/main.cpp | 4 +- apps/navmeshtool/main.cpp | 4 +- .../nifosg/testnifloader.cpp | 6 +- components/misc/resourcehelpers.cpp | 5 + components/misc/resourcehelpers.hpp | 1 + components/nifosg/nifloader.cpp | 116 +++++++++++++++++- components/nifosg/nifloader.hpp | 3 +- components/resource/resourcesystem.cpp | 10 +- components/resource/resourcesystem.hpp | 3 + components/resource/scenemanager.cpp | 12 +- components/resource/scenemanager.hpp | 4 +- 11 files changed, 151 insertions(+), 17 deletions(-) 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; From 96f5ae5a8dae3afe522e9fd2ea5d30c9abb8a5b9 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 18 Apr 2024 07:19:17 +0300 Subject: [PATCH 11/24] Handle BGSM decal flag, hide visibility editor markers --- components/nifosg/nifloader.cpp | 36 ++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 0150c2dc90..08a4fd5d89 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -773,7 +773,7 @@ namespace NifOsg if (isGeometry && !args.mSkipMeshes) { - bool skip; + bool skip = false; if (args.mNifVersion <= Nif::NIFFile::NIFVersion::VER_MW) { skip = (args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->mName, "tri editormarker")) @@ -781,7 +781,10 @@ namespace NifOsg || Misc::StringUtils::ciStartsWith(nifNode->mName, "tri shadow"); } else - skip = args.mHasMarkers && Misc::StringUtils::ciStartsWith(nifNode->mName, "EditorMarker"); + { + if (args.mHasMarkers) + skip = Misc::StringUtils::ciStartsWith(nifNode->mName, "EditorMarker") || Misc::StringUtils::ciStartsWith(nifNode->mName, "VisibilityEditorMarker"); + } if (!skip) { if (isNiGeometry) @@ -2165,7 +2168,8 @@ namespace NifOsg if (!mMaterialMgr) return; - Bgsm::MaterialFilePtr material = mMaterialMgr->get(VFS::Path::Normalized(path)); + std::string normalizedPath = Misc::ResourceHelpers::correctMaterialPath(path, mMaterialMgr->getVFS()); + Bgsm::MaterialFilePtr material = mMaterialMgr->get(VFS::Path::Normalized(normalizedPath)); if (!material) return; @@ -2211,12 +2215,8 @@ namespace NifOsg 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 { @@ -2247,8 +2247,18 @@ namespace NifOsg stateset->addUniform(new osg::Uniform("useFalloff", useFalloff)); if (useFalloff) stateset->addUniform(new osg::Uniform("falloffParams", bgem->mFalloffParams)); - handleDepthFlags(stateset, bgem->mDepthTest, bgem->mDepthWrite); } + + if (material->mTwoSided) + stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); + if (material->mDecal) + { + osg::ref_ptr polygonOffset(new osg::PolygonOffset); + polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f); + polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f); + stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); + } + handleDepthFlags(stateset, material->mDepthTest, material->mDepthWrite); } void handleTextureSet(const Nif::BSShaderTextureSet* textureSet, unsigned int clamp, @@ -2517,10 +2527,9 @@ 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")) + if (Misc::StringUtils::ciEndsWith(texprop->mName, ".bgsm")) { - handleShaderMaterial(normalizedName, stateset, imageManager, boundTextures); + handleShaderMaterial(texprop->mName, stateset, imageManager, boundTextures); break; } if (!texprop->mTextureSet.empty()) @@ -2544,10 +2553,9 @@ 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")) + if (Misc::StringUtils::ciEndsWith(texprop->mName, ".bgem")) { - handleShaderMaterial(normalizedName, stateset, imageManager, boundTextures); + handleShaderMaterial(texprop->mName, stateset, imageManager, boundTextures); break; } if (!texprop->mSourceTexture.empty()) From 1d65aaee714346bcff899f45a64abed2f48dda77 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 18 Apr 2024 07:43:33 +0300 Subject: [PATCH 12/24] Formatting --- apps/niftest/niftest.cpp | 1 - components/bgsm/stream.hpp | 5 ++--- components/nifosg/nifloader.cpp | 20 ++++++++++++-------- components/nifosg/nifloader.hpp | 3 ++- components/resource/resourcesystem.cpp | 3 ++- components/resource/scenemanager.cpp | 3 ++- 6 files changed, 20 insertions(+), 15 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index 3cd4382121..c364303376 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -89,7 +89,6 @@ void readNIF( } } - void readMaterial( const std::filesystem::path& source, const std::filesystem::path& path, const VFS::Manager* vfs, bool quiet) { diff --git a/components/bgsm/stream.hpp b/components/bgsm/stream.hpp index 8b0e1efdbe..2e03a52dd4 100644 --- a/components/bgsm/stream.hpp +++ b/components/bgsm/stream.hpp @@ -3,9 +3,9 @@ #include #include +#include #include #include -#include #include #include #include @@ -58,8 +58,7 @@ namespace Bgsm Files::IStreamPtr mStream; public: - explicit BGSMStream( - const Reader& reader, Files::IStreamPtr&& stream) + explicit BGSMStream(const Reader& reader, Files::IStreamPtr&& stream) : mReader(reader) , mStream(std::move(stream)) { diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 08a4fd5d89..657d053706 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -240,7 +240,8 @@ namespace NifOsg { public: /// @param filename used for warning messages. - LoaderImpl(const std::filesystem::path& filename, unsigned int ver, unsigned int userver, unsigned int bethver, Resource::BgsmFileManager* materialMgr) + LoaderImpl(const std::filesystem::path& filename, unsigned int ver, unsigned int userver, unsigned int bethver, + Resource::BgsmFileManager* materialMgr) : mFilename(filename) , mVersion(ver) , mUserVersion(userver) @@ -783,7 +784,8 @@ namespace NifOsg else { if (args.mHasMarkers) - skip = Misc::StringUtils::ciStartsWith(nifNode->mName, "EditorMarker") || Misc::StringUtils::ciStartsWith(nifNode->mName, "VisibilityEditorMarker"); + skip = Misc::StringUtils::ciStartsWith(nifNode->mName, "EditorMarker") + || Misc::StringUtils::ciStartsWith(nifNode->mName, "VisibilityEditorMarker"); } if (!skip) { @@ -2162,8 +2164,8 @@ namespace NifOsg handleTextureControllers(texprop, composite, imageManager, stateset, animflags); } - void handleShaderMaterial(const std::string& path, osg::StateSet* stateset, Resource::ImageManager* imageManager, - std::vector& boundTextures) + void handleShaderMaterial(const std::string& path, osg::StateSet* stateset, + Resource::ImageManager* imageManager, std::vector& boundTextures) { if (!mMaterialMgr) return; @@ -2229,8 +2231,8 @@ namespace NifOsg stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF); boundTextures.clear(); } - std::string filename = Misc::ResourceHelpers::correctTexturePath( - bgem->mBaseMap, imageManager->getVFS()); + 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"); @@ -2976,9 +2978,11 @@ namespace NifOsg } }; - osg::ref_ptr Loader::load(Nif::FileView file, Resource::ImageManager* imageManager, Resource::BgsmFileManager* materialMgr) + osg::ref_ptr Loader::load( + Nif::FileView file, Resource::ImageManager* imageManager, Resource::BgsmFileManager* materialMgr) { - LoaderImpl impl(file.getFilename(), file.getVersion(), file.getUserVersion(), file.getBethVersion(), materialMgr); + LoaderImpl impl( + file.getFilename(), file.getVersion(), file.getUserVersion(), file.getBethVersion(), materialMgr); return impl.load(file, imageManager); } diff --git a/components/nifosg/nifloader.hpp b/components/nifosg/nifloader.hpp index b016248f07..14f16088cc 100644 --- a/components/nifosg/nifloader.hpp +++ b/components/nifosg/nifloader.hpp @@ -31,7 +31,8 @@ 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, Resource::BgsmFileManager* materialManager); + 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 33bba791a8..f012627efb 100644 --- a/components/resource/resourcesystem.cpp +++ b/components/resource/resourcesystem.cpp @@ -18,7 +18,8 @@ namespace Resource 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(), mBgsmFileManager.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()); diff --git a/components/resource/scenemanager.cpp b/components/resource/scenemanager.cpp index daeafeaf1d..690e38bbf8 100644 --- a/components/resource/scenemanager.cpp +++ b/components/resource/scenemanager.cpp @@ -797,7 +797,8 @@ namespace Resource } osg::ref_ptr load(VFS::Path::NormalizedView normalizedFilename, const VFS::Manager* vfs, - Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager, Resource::BgsmFileManager* materialMgr) + Resource::ImageManager* imageManager, Resource::NifFileManager* nifFileManager, + Resource::BgsmFileManager* materialMgr) { const std::string_view ext = Misc::getFileExtension(normalizedFilename.value()); if (ext == "nif") From 8997bd68544d6a7e2e62593f49d6df63591a52f7 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Thu, 18 Apr 2024 10:10:03 +0300 Subject: [PATCH 13/24] Apply shader material transparency parameters, get rid of unwanted shiny --- components/nifosg/nifloader.cpp | 122 +++++++++++++++++++++++++++----- 1 file changed, 105 insertions(+), 17 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 657d053706..81aeb7a2fc 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -2164,17 +2164,21 @@ namespace NifOsg handleTextureControllers(texprop, composite, imageManager, stateset, animflags); } - void handleShaderMaterial(const std::string& path, osg::StateSet* stateset, - Resource::ImageManager* imageManager, std::vector& boundTextures) + Bgsm::MaterialFilePtr getShaderMaterial(const std::string& path) { if (!mMaterialMgr) - return; + return nullptr; + + if (!Misc::StringUtils::ciEndsWith(path, ".bgem") && !Misc::StringUtils::ciEndsWith(path, ".bgsm")) + return nullptr; std::string normalizedPath = Misc::ResourceHelpers::correctMaterialPath(path, mMaterialMgr->getVFS()); - Bgsm::MaterialFilePtr material = mMaterialMgr->get(VFS::Path::Normalized(normalizedPath)); - if (!material) - return; + return mMaterialMgr->get(VFS::Path::Normalized(normalizedPath)); + } + void handleShaderMaterial(Bgsm::MaterialFilePtr material, osg::StateSet* stateset, + Resource::ImageManager* imageManager, std::vector& boundTextures) + { if (material->mShaderType == Bgsm::ShaderType::Lighting) { const Bgsm::BGSMFile* bgsm = static_cast(material.get()); @@ -2253,13 +2257,6 @@ namespace NifOsg if (material->mTwoSided) stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); - if (material->mDecal) - { - osg::ref_ptr polygonOffset(new osg::PolygonOffset); - polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f); - polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f); - stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); - } handleDepthFlags(stateset, material->mDepthTest, material->mDepthWrite); } @@ -2529,9 +2526,10 @@ namespace NifOsg node->setUserValue("shaderPrefix", std::string(getBSLightingShaderPrefix(texprop->mType))); node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); - if (Misc::StringUtils::ciEndsWith(texprop->mName, ".bgsm")) + Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName); + if (material) { - handleShaderMaterial(texprop->mName, stateset, imageManager, boundTextures); + handleShaderMaterial(material, stateset, imageManager, boundTextures); break; } if (!texprop->mTextureSet.empty()) @@ -2555,9 +2553,10 @@ namespace NifOsg node->setUserValue("shaderPrefix", std::string("bs/nolighting")); node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); - if (Misc::StringUtils::ciEndsWith(texprop->mName, ".bgem")) + Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName); + if (material) { - handleShaderMaterial(texprop->mName, stateset, imageManager, boundTextures); + handleShaderMaterial(material, stateset, imageManager, boundTextures); break; } if (!texprop->mSourceTexture.empty()) @@ -2832,6 +2831,52 @@ namespace NifOsg case Nif::RC_BSLightingShaderProperty: { auto shaderprop = static_cast(property); + Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName); + if (shaderMat) + { + mat->setAlpha(osg::Material::FRONT_AND_BACK, shaderMat->mTransparency); + if (shaderMat->mAlphaTest) + { + osg::StateSet* stateset = node->getOrCreateStateSet(); + osg::ref_ptr alphaFunc(new osg::AlphaFunc( + osg::AlphaFunc::GREATER, shaderMat->mAlphaTestThreshold / 255.f)); + alphaFunc = shareAttribute(alphaFunc); + stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); + } + if (shaderMat->mAlphaBlend) + { + osg::StateSet* stateset = node->getOrCreateStateSet(); + osg::ref_ptr blendFunc( + new osg::BlendFunc(getBlendMode(shaderMat->mSourceBlendMode), + getBlendMode(shaderMat->mDestinationBlendMode))); + blendFunc = shareAttribute(blendFunc); + stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); + hasSortAlpha = true; + if (!mPushedSorter) + setBin_Transparent(stateset); + } + if (shaderMat->mDecal) + { + osg::StateSet* stateset = node->getOrCreateStateSet(); + if (!mPushedSorter && !hasSortAlpha) + stateset->setRenderBinDetails(1, "SORT_BACK_TO_FRONT"); + osg::ref_ptr polygonOffset(new osg::PolygonOffset); + polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f); + polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f); + stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); + } + if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting) + { + auto bgsm = static_cast(shaderMat.get()); + specEnabled + = false; // bgsm->mSpecularEnabled; disabled until it can be implemented properly + specStrength = bgsm->mSpecularMult; + emissiveMult = bgsm->mEmittanceMult; + mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgsm->mEmittanceColor, 1.f)); + mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgsm->mSpecularColor, 1.f)); + } + break; + } mat->setAlpha(osg::Material::FRONT_AND_BACK, shaderprop->mAlpha); mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(shaderprop->mEmissive, 1.f)); mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(shaderprop->mSpecular, 1.f)); @@ -2855,6 +2900,49 @@ namespace NifOsg case Nif::RC_BSEffectShaderProperty: { auto shaderprop = static_cast(property); + Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName); + if (shaderMat) + { + mat->setAlpha(osg::Material::FRONT_AND_BACK, shaderMat->mTransparency); + if (shaderMat->mAlphaTest) + { + osg::StateSet* stateset = node->getOrCreateStateSet(); + osg::ref_ptr alphaFunc(new osg::AlphaFunc( + osg::AlphaFunc::GREATER, shaderMat->mAlphaTestThreshold / 255.f)); + alphaFunc = shareAttribute(alphaFunc); + stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); + } + if (shaderMat->mAlphaBlend) + { + osg::StateSet* stateset = node->getOrCreateStateSet(); + osg::ref_ptr blendFunc( + new osg::BlendFunc(getBlendMode(shaderMat->mSourceBlendMode), + getBlendMode(shaderMat->mDestinationBlendMode))); + blendFunc = shareAttribute(blendFunc); + stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); + hasSortAlpha = true; + if (!mPushedSorter) + setBin_Transparent(stateset); + } + if (shaderMat->mDecal) + { + osg::StateSet* stateset = node->getOrCreateStateSet(); + if (!mPushedSorter && !hasSortAlpha) + stateset->setRenderBinDetails(1, "SORT_BACK_TO_FRONT"); + osg::ref_ptr polygonOffset(new osg::PolygonOffset); + polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f); + polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f); + stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); + } + if (shaderMat->mShaderType == Bgsm::ShaderType::Effect) + { + auto bgem = static_cast(shaderMat.get()); + mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgem->mEmittanceColor, 1.f)); + if (bgem->mSoft) + SceneUtil::setupSoftEffect(*node, bgem->mSoftDepth, true, bgem->mSoftDepth); + } + break; + } if (shaderprop->decal()) { osg::StateSet* stateset = node->getOrCreateStateSet(); From e680123482190419db2ffa54315ea2a8479ae7d1 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Fri, 19 Apr 2024 13:35:29 +0300 Subject: [PATCH 14/24] NifLoader: Make the image manager a member --- components/nifosg/nifloader.cpp | 125 +++++++++++++++----------------- 1 file changed, 57 insertions(+), 68 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 81aeb7a2fc..6f630b4dd5 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -240,18 +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, - Resource::BgsmFileManager* materialMgr) + LoaderImpl(const std::filesystem::path& filename, unsigned int ver, unsigned int userver, unsigned int bethver) : mFilename(filename) , mVersion(ver) , mUserVersion(userver) , mBethVersion(bethver) - , mMaterialMgr(materialMgr) { } std::filesystem::path mFilename; unsigned int mVersion, mUserVersion, mBethVersion; - Resource::BgsmFileManager* mMaterialMgr; + Resource::BgsmFileManager* mMaterialManager{ nullptr }; + Resource::ImageManager* mImageManager{ nullptr }; size_t mFirstRootTextureIndex{ ~0u }; bool mFoundFirstRootTexturingProperty = false; @@ -344,7 +343,6 @@ namespace NifOsg struct HandleNodeArgs { unsigned int mNifVersion; - Resource::ImageManager* mImageManager; SceneUtil::TextKeyMap* mTextKeys; std::vector mBoundTextures = {}; int mAnimFlags = 0; @@ -354,7 +352,7 @@ namespace NifOsg osg::Node* mRootNode = nullptr; }; - osg::ref_ptr load(Nif::FileView nif, Resource::ImageManager* imageManager) + osg::ref_ptr load(Nif::FileView nif) { const size_t numRoots = nif.numRoots(); std::vector roots; @@ -376,10 +374,8 @@ namespace NifOsg created->setDataVariance(osg::Object::STATIC); for (const Nif::NiAVObject* root : roots) { - auto node = handleNode(root, nullptr, nullptr, - { .mNifVersion = nif.getVersion(), - .mImageManager = imageManager, - .mTextKeys = &textkeys->mTextKeys }); + auto node = handleNode( + root, nullptr, nullptr, { .mNifVersion = nif.getVersion(), .mTextKeys = &textkeys->mTextKeys }); created->addChild(node); } if (mHasNightDayLabel) @@ -410,8 +406,7 @@ namespace NifOsg } void applyNodeProperties(const Nif::NiAVObject* nifNode, osg::Node* applyTo, - SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, - std::vector& boundTextures, int animflags) + SceneUtil::CompositeStateSetUpdater* composite, std::vector& boundTextures, int animflags) { bool hasStencilProperty = false; @@ -449,8 +444,7 @@ namespace NifOsg if (property.getPtr()->recIndex == mFirstRootTextureIndex) applyTo->setUserValue("overrideFx", 1); } - handleProperty(property.getPtr(), applyTo, composite, imageManager, boundTextures, animflags, - hasStencilProperty); + handleProperty(property.getPtr(), applyTo, composite, boundTextures, animflags, hasStencilProperty); } } @@ -462,8 +456,7 @@ namespace NifOsg shaderprop = static_cast(nifNode)->mShaderProperty; if (!shaderprop.empty()) - handleProperty(shaderprop.getPtr(), applyTo, composite, imageManager, boundTextures, animflags, - hasStencilProperty); + handleProperty(shaderprop.getPtr(), applyTo, composite, boundTextures, animflags, hasStencilProperty); } static void setupController(const Nif::NiTimeController* ctrl, SceneUtil::Controller* toSetup, int animflags) @@ -527,8 +520,7 @@ namespace NifOsg sequenceNode->setMode(osg::Sequence::START); } - osg::ref_ptr handleSourceTexture( - const Nif::NiSourceTexture* st, Resource::ImageManager* imageManager) + osg::ref_ptr handleSourceTexture(const Nif::NiSourceTexture* st) { if (!st) return nullptr; @@ -536,8 +528,8 @@ namespace NifOsg osg::ref_ptr image; if (st->mExternal) { - std::string filename = Misc::ResourceHelpers::correctTexturePath(st->mFile, imageManager->getVFS()); - image = imageManager->getImage(filename); + std::string filename = Misc::ResourceHelpers::correctTexturePath(st->mFile, mImageManager->getVFS()); + image = mImageManager->getImage(filename); } else if (!st->mData.empty()) { @@ -552,7 +544,7 @@ namespace NifOsg texture->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); } - bool handleEffect(const Nif::NiAVObject* nifNode, osg::StateSet* stateset, Resource::ImageManager* imageManager) + bool handleEffect(const Nif::NiAVObject* nifNode, osg::StateSet* stateset) { if (nifNode->recType != Nif::RC_NiTextureEffect) { @@ -595,7 +587,7 @@ namespace NifOsg return false; } - osg::ref_ptr image(handleSourceTexture(textureEffect->mTexture.getPtr(), imageManager)); + osg::ref_ptr image(handleSourceTexture(textureEffect->mTexture.getPtr())); osg::ref_ptr texture2d(new osg::Texture2D(image)); if (image) texture2d->setTextureSize(image->s(), image->t()); @@ -766,7 +758,7 @@ namespace NifOsg osg::ref_ptr composite = new SceneUtil::CompositeStateSetUpdater; - applyNodeProperties(nifNode, node, composite, args.mImageManager, args.mBoundTextures, args.mAnimFlags); + applyNodeProperties(nifNode, node, composite, args.mBoundTextures, args.mAnimFlags); const bool isNiGeometry = isTypeNiGeometry(nifNode->recType); const bool isBSGeometry = isTypeBSGeometry(nifNode->recType); @@ -868,7 +860,7 @@ namespace NifOsg if (!effect.empty()) { osg::ref_ptr effectStateSet = new osg::StateSet; - if (handleEffect(effect.getPtr(), effectStateSet, args.mImageManager)) + if (handleEffect(effect.getPtr(), effectStateSet)) for (unsigned int i = 0; i < currentNode->getNumChildren(); ++i) currentNode->getChild(i)->getOrCreateStateSet()->merge(*effectStateSet); } @@ -1035,8 +1027,7 @@ namespace NifOsg } void handleTextureControllers(const Nif::NiProperty* texProperty, - SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, - osg::StateSet* stateset, int animflags) + SceneUtil::CompositeStateSetUpdater* composite, osg::StateSet* stateset, int animflags) { for (Nif::NiTimeControllerPtr ctrl = texProperty->mController; !ctrl.empty(); ctrl = ctrl->mNext) { @@ -1070,7 +1061,7 @@ namespace NifOsg if (source.empty()) continue; - osg::ref_ptr image(handleSourceTexture(source.getPtr(), imageManager)); + osg::ref_ptr image(handleSourceTexture(source.getPtr())); osg::ref_ptr texture(new osg::Texture2D(image)); if (image) texture->setTextureSize(image->s(), image->t()); @@ -1986,7 +1977,7 @@ namespace NifOsg void handleTextureProperty(const Nif::NiTexturingProperty* texprop, const std::string& nodeName, osg::StateSet* stateset, SceneUtil::CompositeStateSetUpdater* composite, - Resource::ImageManager* imageManager, std::vector& boundTextures, int animflags) + std::vector& boundTextures, int animflags) { if (!boundTextures.empty()) { @@ -2039,7 +2030,7 @@ namespace NifOsg if (!tex.mSourceTexture.empty()) { const Nif::NiSourceTexture* st = tex.mSourceTexture.getPtr(); - osg::ref_ptr image = handleSourceTexture(st, imageManager); + osg::ref_ptr image = handleSourceTexture(st); texture2d = new osg::Texture2D(image); if (image) texture2d->setTextureSize(image->s(), image->t()); @@ -2161,23 +2152,23 @@ namespace NifOsg boundTextures.push_back(uvSet); } } - handleTextureControllers(texprop, composite, imageManager, stateset, animflags); + handleTextureControllers(texprop, composite, stateset, animflags); } Bgsm::MaterialFilePtr getShaderMaterial(const std::string& path) { - if (!mMaterialMgr) + if (!mMaterialManager) return nullptr; if (!Misc::StringUtils::ciEndsWith(path, ".bgem") && !Misc::StringUtils::ciEndsWith(path, ".bgsm")) return nullptr; - std::string normalizedPath = Misc::ResourceHelpers::correctMaterialPath(path, mMaterialMgr->getVFS()); - return mMaterialMgr->get(VFS::Path::Normalized(normalizedPath)); + std::string normalizedPath = Misc::ResourceHelpers::correctMaterialPath(path, mMaterialManager->getVFS()); + return mMaterialManager->get(VFS::Path::Normalized(normalizedPath)); } - void handleShaderMaterial(Bgsm::MaterialFilePtr material, osg::StateSet* stateset, - Resource::ImageManager* imageManager, std::vector& boundTextures) + void handleShaderMaterial( + Bgsm::MaterialFilePtr material, osg::StateSet* stateset, std::vector& boundTextures) { if (material->mShaderType == Bgsm::ShaderType::Lighting) { @@ -2194,8 +2185,8 @@ namespace NifOsg if (!bgsm->mDiffuseMap.empty()) { std::string filename - = Misc::ResourceHelpers::correctTexturePath(bgsm->mDiffuseMap, imageManager->getVFS()); - osg::ref_ptr image = imageManager->getImage(filename); + = Misc::ResourceHelpers::correctTexturePath(bgsm->mDiffuseMap, mImageManager->getVFS()); + osg::ref_ptr image = mImageManager->getImage(filename); osg::ref_ptr texture2d = new osg::Texture2D(image); if (image) texture2d->setTextureSize(image->s(), image->t()); @@ -2209,8 +2200,8 @@ namespace NifOsg if (!bgsm->mNormalMap.empty()) { std::string filename - = Misc::ResourceHelpers::correctTexturePath(bgsm->mNormalMap, imageManager->getVFS()); - osg::ref_ptr image = imageManager->getImage(filename); + = Misc::ResourceHelpers::correctTexturePath(bgsm->mNormalMap, mImageManager->getVFS()); + osg::ref_ptr image = mImageManager->getImage(filename); osg::ref_ptr texture2d = new osg::Texture2D(image); if (image) texture2d->setTextureSize(image->s(), image->t()); @@ -2236,8 +2227,8 @@ namespace NifOsg boundTextures.clear(); } std::string filename - = Misc::ResourceHelpers::correctTexturePath(bgem->mBaseMap, imageManager->getVFS()); - osg::ref_ptr image = imageManager->getImage(filename); + = Misc::ResourceHelpers::correctTexturePath(bgem->mBaseMap, mImageManager->getVFS()); + osg::ref_ptr image = mImageManager->getImage(filename); osg::ref_ptr texture2d = new osg::Texture2D(image); texture2d->setName("diffuseMap"); if (image) @@ -2261,8 +2252,7 @@ namespace NifOsg } void handleTextureSet(const Nif::BSShaderTextureSet* textureSet, unsigned int clamp, - const std::string& nodeName, osg::StateSet* stateset, Resource::ImageManager* imageManager, - std::vector& boundTextures) + const std::string& nodeName, osg::StateSet* stateset, std::vector& boundTextures) { if (!boundTextures.empty()) { @@ -2291,8 +2281,8 @@ namespace NifOsg } } std::string filename - = Misc::ResourceHelpers::correctTexturePath(textureSet->mTextures[i], imageManager->getVFS()); - osg::ref_ptr image = imageManager->getImage(filename); + = Misc::ResourceHelpers::correctTexturePath(textureSet->mTextures[i], mImageManager->getVFS()); + osg::ref_ptr image = mImageManager->getImage(filename); osg::ref_ptr texture2d = new osg::Texture2D(image); if (image) texture2d->setTextureSize(image->s(), image->t()); @@ -2374,8 +2364,8 @@ namespace NifOsg } void handleProperty(const Nif::NiProperty* property, osg::Node* node, - SceneUtil::CompositeStateSetUpdater* composite, Resource::ImageManager* imageManager, - std::vector& boundTextures, int animflags, bool hasStencilProperty) + SceneUtil::CompositeStateSetUpdater* composite, std::vector& boundTextures, int animflags, + bool hasStencilProperty) { switch (property->recType) { @@ -2457,8 +2447,7 @@ namespace NifOsg { const Nif::NiTexturingProperty* texprop = static_cast(property); osg::StateSet* stateset = node->getOrCreateStateSet(); - handleTextureProperty( - texprop, node->getName(), stateset, composite, imageManager, boundTextures, animflags); + handleTextureProperty(texprop, node->getName(), stateset, composite, boundTextures, animflags); node->setUserValue("applyMode", static_cast(texprop->mApplyMode)); break; } @@ -2472,10 +2461,9 @@ namespace NifOsg if (!texprop->mTextureSet.empty()) { auto textureSet = texprop->mTextureSet.getPtr(); - handleTextureSet( - textureSet, texprop->mClamp, node->getName(), stateset, imageManager, boundTextures); + handleTextureSet(textureSet, texprop->mClamp, node->getName(), stateset, boundTextures); } - handleTextureControllers(texprop, composite, imageManager, stateset, animflags); + handleTextureControllers(texprop, composite, stateset, animflags); if (texprop->refraction()) SceneUtil::setupDistortion(*node, texprop->mRefraction.mStrength); break; @@ -2497,8 +2485,8 @@ namespace NifOsg boundTextures.clear(); } std::string filename - = Misc::ResourceHelpers::correctTexturePath(texprop->mFilename, imageManager->getVFS()); - osg::ref_ptr image = imageManager->getImage(filename); + = Misc::ResourceHelpers::correctTexturePath(texprop->mFilename, mImageManager->getVFS()); + osg::ref_ptr image = mImageManager->getImage(filename); osg::ref_ptr texture2d = new osg::Texture2D(image); texture2d->setName("diffuseMap"); if (image) @@ -2515,7 +2503,7 @@ namespace NifOsg } } stateset->addUniform(new osg::Uniform("useFalloff", useFalloff)); - handleTextureControllers(texprop, composite, imageManager, stateset, animflags); + handleTextureControllers(texprop, composite, stateset, animflags); handleDepthFlags(stateset, texprop->depthTest(), texprop->depthWrite()); break; } @@ -2529,13 +2517,13 @@ namespace NifOsg Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName); if (material) { - handleShaderMaterial(material, stateset, imageManager, boundTextures); + handleShaderMaterial(material, stateset, boundTextures); break; } if (!texprop->mTextureSet.empty()) - handleTextureSet(texprop->mTextureSet.getPtr(), texprop->mClamp, node->getName(), stateset, - imageManager, boundTextures); - handleTextureControllers(texprop, composite, imageManager, stateset, animflags); + handleTextureSet( + texprop->mTextureSet.getPtr(), texprop->mClamp, node->getName(), stateset, boundTextures); + handleTextureControllers(texprop, composite, stateset, animflags); if (texprop->doubleSided()) stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); if (texprop->treeAnim()) @@ -2556,7 +2544,7 @@ namespace NifOsg Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName); if (material) { - handleShaderMaterial(material, stateset, imageManager, boundTextures); + handleShaderMaterial(material, stateset, boundTextures); break; } if (!texprop->mSourceTexture.empty()) @@ -2568,8 +2556,8 @@ namespace NifOsg boundTextures.clear(); } std::string filename = Misc::ResourceHelpers::correctTexturePath( - texprop->mSourceTexture, imageManager->getVFS()); - osg::ref_ptr image = imageManager->getImage(filename); + texprop->mSourceTexture, mImageManager->getVFS()); + osg::ref_ptr image = mImageManager->getImage(filename); osg::ref_ptr texture2d = new osg::Texture2D(image); texture2d->setName("diffuseMap"); if (image) @@ -2601,7 +2589,7 @@ namespace NifOsg stateset->addUniform(new osg::Uniform("useFalloff", useFalloff)); if (useFalloff) stateset->addUniform(new osg::Uniform("falloffParams", texprop->mFalloffParams)); - handleTextureControllers(texprop, composite, imageManager, stateset, animflags); + handleTextureControllers(texprop, composite, stateset, animflags); if (texprop->doubleSided()) stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); handleDepthFlags(stateset, texprop->depthTest(), texprop->depthWrite()); @@ -3067,16 +3055,17 @@ namespace NifOsg }; osg::ref_ptr Loader::load( - Nif::FileView file, Resource::ImageManager* imageManager, Resource::BgsmFileManager* materialMgr) + Nif::FileView file, Resource::ImageManager* imageManager, Resource::BgsmFileManager* materialManager) { - LoaderImpl impl( - file.getFilename(), file.getVersion(), file.getUserVersion(), file.getBethVersion(), materialMgr); - return impl.load(file, imageManager); + LoaderImpl impl(file.getFilename(), file.getVersion(), file.getUserVersion(), file.getBethVersion()); + impl.mMaterialManager = materialManager; + impl.mImageManager = imageManager; + return impl.load(file); } void Loader::loadKf(Nif::FileView kf, SceneUtil::KeyframeHolder& target) { - LoaderImpl impl(kf.getFilename(), kf.getVersion(), kf.getUserVersion(), kf.getBethVersion(), nullptr); + LoaderImpl impl(kf.getFilename(), kf.getVersion(), kf.getUserVersion(), kf.getBethVersion()); impl.loadKf(kf, target); } From 4ccf9c1917c0ebc0d1b7219469a41d438c1475c4 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Fri, 19 Apr 2024 16:07:21 +0300 Subject: [PATCH 15/24] Deduplicate NifLoader texture attachment Handle non-existent shader materials more gracefully Deduplicate shader material drawable property handling --- components/nifosg/nifloader.cpp | 515 +++++++++++++------------------- 1 file changed, 202 insertions(+), 313 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 6f630b4dd5..7177377771 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -520,28 +520,18 @@ namespace NifOsg sequenceNode->setMode(osg::Sequence::START); } - osg::ref_ptr handleSourceTexture(const Nif::NiSourceTexture* st) + osg::ref_ptr handleSourceTexture(const Nif::NiSourceTexture* st) const { - if (!st) - return nullptr; - - osg::ref_ptr image; - if (st->mExternal) - { - std::string filename = Misc::ResourceHelpers::correctTexturePath(st->mFile, mImageManager->getVFS()); - image = mImageManager->getImage(filename); - } - else if (!st->mData.empty()) + if (st) { - image = handleInternalTexture(st->mData.getPtr()); + if (st->mExternal) + return getTextureImage(st->mFile); + + if (!st->mData.empty()) + return handleInternalTexture(st->mData.getPtr()); } - return image; - } - void handleTextureWrapping(osg::Texture2D* texture, bool wrapS, bool wrapT) - { - texture->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); - texture->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + return nullptr; } bool handleEffect(const Nif::NiAVObject* nifNode, osg::StateSet* stateset) @@ -587,16 +577,12 @@ namespace NifOsg return false; } - osg::ref_ptr image(handleSourceTexture(textureEffect->mTexture.getPtr())); - osg::ref_ptr texture2d(new osg::Texture2D(image)); - if (image) - texture2d->setTextureSize(image->s(), image->t()); - texture2d->setName("envMap"); - handleTextureWrapping(texture2d, textureEffect->wrapS(), textureEffect->wrapT()); - - int texUnit = 3; // FIXME - - stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); + const unsigned int uvSet = 0; + const unsigned int texUnit = 3; // FIXME + std::vector boundTextures; + boundTextures.resize(3); // Dummy vector for attachNiSourceTexture + attachNiSourceTexture("envMap", textureEffect->mTexture.getPtr(), textureEffect->wrapS(), + textureEffect->wrapT(), uvSet, stateset, boundTextures); stateset->setTextureAttributeAndModes(texUnit, texGen, osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(texUnit, createEmissiveTexEnv(), osg::StateAttribute::ON); @@ -1026,6 +1012,54 @@ namespace NifOsg } } + osg::ref_ptr getTextureImage(std::string_view path) const + { + if (!mImageManager) + return nullptr; + + std::string filename = Misc::ResourceHelpers::correctTexturePath(path, mImageManager->getVFS()); + return mImageManager->getImage(filename); + } + + osg::ref_ptr attachTexture(const std::string& name, osg::ref_ptr image, bool wrapS, + bool wrapT, unsigned int uvSet, osg::StateSet* stateset, std::vector& boundTextures) const + { + osg::ref_ptr texture2d = new osg::Texture2D(image); + if (image) + texture2d->setTextureSize(image->s(), image->t()); + texture2d->setWrap(osg::Texture::WRAP_S, wrapS ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + texture2d->setWrap(osg::Texture::WRAP_T, wrapT ? osg::Texture::REPEAT : osg::Texture::CLAMP_TO_EDGE); + unsigned int texUnit = boundTextures.size(); + if (stateset) + stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); + texture2d->setName(name); + boundTextures.emplace_back(uvSet); + return texture2d; + } + + osg::ref_ptr attachExternalTexture(const std::string& name, const std::string& path, bool wrapS, + bool wrapT, unsigned int uvSet, osg::StateSet* stateset, std::vector& boundTextures) const + { + return attachTexture(name, getTextureImage(path), wrapS, wrapT, uvSet, stateset, boundTextures); + } + + osg::ref_ptr attachNiSourceTexture(const std::string& name, const Nif::NiSourceTexture* st, + bool wrapS, bool wrapT, unsigned int uvSet, osg::StateSet* stateset, + std::vector& boundTextures) const + { + return attachTexture(name, handleSourceTexture(st), wrapS, wrapT, uvSet, stateset, boundTextures); + } + + static void clearBoundTextures(osg::StateSet* stateset, std::vector& boundTextures) + { + if (!boundTextures.empty()) + { + for (unsigned int i = 0; i < boundTextures.size(); ++i) + stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF); + boundTextures.clear(); + } + } + void handleTextureControllers(const Nif::NiProperty* texProperty, SceneUtil::CompositeStateSetUpdater* composite, osg::StateSet* stateset, int animflags) { @@ -1056,17 +1090,16 @@ namespace NifOsg wrapT = inherit->getWrap(osg::Texture2D::WRAP_T); } + const unsigned int uvSet = 0; + std::vector boundTextures; // Dummy list for attachTexture for (const auto& source : flipctrl->mSources) { if (source.empty()) continue; - osg::ref_ptr image(handleSourceTexture(source.getPtr())); - osg::ref_ptr texture(new osg::Texture2D(image)); - if (image) - texture->setTextureSize(image->s(), image->t()); - texture->setWrap(osg::Texture::WRAP_S, wrapS); - texture->setWrap(osg::Texture::WRAP_T, wrapT); + // NB: not changing the stateset + osg::ref_ptr texture + = attachNiSourceTexture({}, source.getPtr(), wrapS, wrapT, uvSet, nullptr, boundTextures); textures.push_back(texture); } osg::ref_ptr callback(new FlipController(flipctrl, textures)); @@ -1811,7 +1844,7 @@ namespace NifOsg } } - osg::ref_ptr handleInternalTexture(const Nif::NiPixelData* pixelData) + osg::ref_ptr handleInternalTexture(const Nif::NiPixelData* pixelData) const { if (pixelData->mMipmaps.empty()) return nullptr; @@ -1946,7 +1979,7 @@ namespace NifOsg return image; } - osg::ref_ptr createEmissiveTexEnv() + static osg::ref_ptr createEmissiveTexEnv() { osg::ref_ptr texEnv(new osg::TexEnvCombine); // Sum the previous colour and the emissive colour. @@ -1979,31 +2012,40 @@ namespace NifOsg osg::StateSet* stateset, SceneUtil::CompositeStateSetUpdater* composite, std::vector& boundTextures, int animflags) { - if (!boundTextures.empty()) - { - // overriding a parent NiTexturingProperty, so remove what was previously bound - for (unsigned int i = 0; i < boundTextures.size(); ++i) - stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF); - boundTextures.clear(); - } + // overriding a parent NiTexturingProperty, so remove what was previously bound + clearBoundTextures(stateset, boundTextures); // If this loop is changed such that the base texture isn't guaranteed to end up in texture unit 0, the // shadow casting shader will need to be updated accordingly. for (size_t i = 0; i < texprop->mTextures.size(); ++i) { - if (texprop->mTextures[i].mEnabled - || (i == Nif::NiTexturingProperty::BaseTexture && !texprop->mController.empty())) + const Nif::NiTexturingProperty::Texture& tex = texprop->mTextures[i]; + if (tex.mEnabled || (i == Nif::NiTexturingProperty::BaseTexture && !texprop->mController.empty())) { + std::string textureName; switch (i) { // These are handled later on case Nif::NiTexturingProperty::BaseTexture: + textureName = "diffuseMap"; + break; case Nif::NiTexturingProperty::GlowTexture: + textureName = "glowMap"; + break; case Nif::NiTexturingProperty::DarkTexture: + textureName = "darkMap"; + break; case Nif::NiTexturingProperty::BumpTexture: + textureName = "bumpMap"; + break; case Nif::NiTexturingProperty::DetailTexture: + textureName = "detailMap"; + break; case Nif::NiTexturingProperty::DecalTexture: + textureName = "decalMap"; + break; case Nif::NiTexturingProperty::GlossTexture: + textureName = "glossMap"; break; default: { @@ -2013,12 +2055,9 @@ namespace NifOsg } } - unsigned int uvSet = 0; - // create a new texture, will later attempt to share using the SharedStateManager - osg::ref_ptr texture2d; - if (texprop->mTextures[i].mEnabled) + const unsigned int texUnit = boundTextures.size(); + if (tex.mEnabled) { - const Nif::NiTexturingProperty::Texture& tex = texprop->mTextures[i]; if (tex.mSourceTexture.empty() && texprop->mController.empty()) { if (i == 0) @@ -2028,32 +2067,18 @@ namespace NifOsg } if (!tex.mSourceTexture.empty()) - { - const Nif::NiSourceTexture* st = tex.mSourceTexture.getPtr(); - osg::ref_ptr image = handleSourceTexture(st); - texture2d = new osg::Texture2D(image); - if (image) - texture2d->setTextureSize(image->s(), image->t()); - } + attachNiSourceTexture(textureName, tex.mSourceTexture.getPtr(), tex.wrapS(), tex.wrapT(), + tex.mUVSet, stateset, boundTextures); else - texture2d = new osg::Texture2D; - - handleTextureWrapping(texture2d, tex.wrapS(), tex.wrapT()); - - uvSet = tex.mUVSet; + attachTexture( + textureName, nullptr, tex.wrapS(), tex.wrapT(), tex.mUVSet, stateset, boundTextures); } else { // Texture only comes from NiFlipController, so tex is ignored, set defaults - texture2d = new osg::Texture2D; - handleTextureWrapping(texture2d, true, true); - uvSet = 0; + attachTexture(textureName, nullptr, true, true, 0, stateset, boundTextures); } - unsigned int texUnit = boundTextures.size(); - - stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); - if (i == Nif::NiTexturingProperty::GlowTexture) { stateset->setTextureAttributeAndModes(texUnit, createEmissiveTexEnv(), osg::StateAttribute::ON); @@ -2121,41 +2146,12 @@ namespace NifOsg texEnv->setOperand0_Alpha(osg::TexEnvCombine::SRC_ALPHA); stateset->setTextureAttributeAndModes(texUnit, texEnv, osg::StateAttribute::ON); } - - switch (i) - { - case Nif::NiTexturingProperty::BaseTexture: - texture2d->setName("diffuseMap"); - break; - case Nif::NiTexturingProperty::BumpTexture: - texture2d->setName("bumpMap"); - break; - case Nif::NiTexturingProperty::GlowTexture: - texture2d->setName("emissiveMap"); - break; - case Nif::NiTexturingProperty::DarkTexture: - texture2d->setName("darkMap"); - break; - case Nif::NiTexturingProperty::DetailTexture: - texture2d->setName("detailMap"); - break; - case Nif::NiTexturingProperty::DecalTexture: - texture2d->setName("decalMap"); - break; - case Nif::NiTexturingProperty::GlossTexture: - texture2d->setName("glossMap"); - break; - default: - break; - } - - boundTextures.push_back(uvSet); } } handleTextureControllers(texprop, composite, stateset, animflags); } - Bgsm::MaterialFilePtr getShaderMaterial(const std::string& path) + Bgsm::MaterialFilePtr getShaderMaterial(std::string_view path) const { if (!mMaterialManager) return nullptr; @@ -2164,81 +2160,43 @@ namespace NifOsg return nullptr; std::string normalizedPath = Misc::ResourceHelpers::correctMaterialPath(path, mMaterialManager->getVFS()); - return mMaterialManager->get(VFS::Path::Normalized(normalizedPath)); + try + { + return mMaterialManager->get(VFS::Path::Normalized(normalizedPath)); + } + catch (std::exception& e) + { + Log(Debug::Error) << "Failed to load shader material: " << e.what(); + return nullptr; + } } - void handleShaderMaterial( + void handleShaderMaterialNodeProperties( Bgsm::MaterialFilePtr material, osg::StateSet* stateset, std::vector& boundTextures) { + const unsigned int uvSet = 0; + const bool wrapS = (material->mClamp >> 1) & 0x1; + const bool wrapT = material->mClamp & 0x1; 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, mImageManager->getVFS()); - osg::ref_ptr image = mImageManager->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); - } + attachExternalTexture( + "diffuseMap", bgsm->mDiffuseMap, wrapS, wrapT, uvSet, stateset, boundTextures); if (!bgsm->mNormalMap.empty()) - { - std::string filename - = Misc::ResourceHelpers::correctTexturePath(bgsm->mNormalMap, mImageManager->getVFS()); - osg::ref_ptr image = mImageManager->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); - } + attachExternalTexture("normalMap", bgsm->mNormalMap, wrapS, wrapT, uvSet, stateset, boundTextures); if (bgsm->mTree) stateset->addUniform(new osg::Uniform("useTreeAnim", true)); } - else + else if (material->mShaderType == Bgsm::ShaderType::Effect) { 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, mImageManager->getVFS()); - osg::ref_ptr image = mImageManager->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); - } + attachExternalTexture("diffuseMap", bgem->mBaseMap, wrapS, wrapT, uvSet, stateset, boundTextures); bool useFalloff = bgem->mFalloff; stateset->addUniform(new osg::Uniform("useFalloff", useFalloff)); @@ -2251,16 +2209,55 @@ namespace NifOsg handleDepthFlags(stateset, material->mDepthTest, material->mDepthWrite); } - void handleTextureSet(const Nif::BSShaderTextureSet* textureSet, unsigned int clamp, - const std::string& nodeName, osg::StateSet* stateset, std::vector& boundTextures) + void handleShaderMaterialDrawableProperties( + Bgsm::MaterialFilePtr shaderMat, osg::ref_ptr mat, osg::Node& node, bool& hasSortAlpha) { - if (!boundTextures.empty()) + mat->setAlpha(osg::Material::FRONT_AND_BACK, shaderMat->mTransparency); + if (shaderMat->mAlphaTest) { - for (unsigned int i = 0; i < boundTextures.size(); ++i) - stateset->setTextureMode(i, GL_TEXTURE_2D, osg::StateAttribute::OFF); - boundTextures.clear(); + osg::StateSet* stateset = node.getOrCreateStateSet(); + osg::ref_ptr alphaFunc( + new osg::AlphaFunc(osg::AlphaFunc::GREATER, shaderMat->mAlphaTestThreshold / 255.f)); + alphaFunc = shareAttribute(alphaFunc); + stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); + } + if (shaderMat->mAlphaBlend) + { + osg::StateSet* stateset = node.getOrCreateStateSet(); + osg::ref_ptr blendFunc(new osg::BlendFunc( + getBlendMode(shaderMat->mSourceBlendMode), getBlendMode(shaderMat->mDestinationBlendMode))); + blendFunc = shareAttribute(blendFunc); + stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); + hasSortAlpha = true; + } + if (shaderMat->mDecal) + { + osg::StateSet* stateset = node.getOrCreateStateSet(); + if (!mPushedSorter && !hasSortAlpha) + stateset->setRenderBinDetails(1, "SORT_BACK_TO_FRONT"); + osg::ref_ptr polygonOffset(new osg::PolygonOffset); + polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f); + polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f); + stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); + } + if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting) + { + auto bgsm = static_cast(shaderMat.get()); + mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgsm->mEmittanceColor, 1.f)); + mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgsm->mSpecularColor, 1.f)); } + else if (shaderMat->mShaderType == Bgsm::ShaderType::Effect) + { + auto bgem = static_cast(shaderMat.get()); + mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgem->mEmittanceColor, 1.f)); + if (bgem->mSoft) + SceneUtil::setupSoftEffect(node, bgem->mSoftDepth, true, bgem->mSoftDepth); + } + } + void handleTextureSet(const Nif::BSShaderTextureSet* textureSet, bool wrapS, bool wrapT, + const std::string& nodeName, osg::StateSet* stateset, std::vector& boundTextures) + { const unsigned int uvSet = 0; for (size_t i = 0; i < textureSet->mTextures.size(); ++i) @@ -2270,8 +2267,16 @@ namespace NifOsg switch (static_cast(i)) { case Nif::BSShaderTextureSet::TextureType::Base: + attachExternalTexture( + "diffuseMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures); + break; case Nif::BSShaderTextureSet::TextureType::Normal: + attachExternalTexture( + "normalMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures); + break; case Nif::BSShaderTextureSet::TextureType::Glow: + attachExternalTexture( + "emissiveMap", textureSet->mTextures[i], wrapS, wrapT, uvSet, stateset, boundTextures); break; default: { @@ -2280,31 +2285,6 @@ namespace NifOsg continue; } } - std::string filename - = Misc::ResourceHelpers::correctTexturePath(textureSet->mTextures[i], mImageManager->getVFS()); - osg::ref_ptr image = mImageManager->getImage(filename); - osg::ref_ptr texture2d = new osg::Texture2D(image); - if (image) - texture2d->setTextureSize(image->s(), image->t()); - handleTextureWrapping(texture2d, (clamp >> 1) & 0x1, clamp & 0x1); - unsigned int texUnit = boundTextures.size(); - stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); - // BSShaderTextureSet presence means there's no need for FFP support for the affected node - switch (static_cast(i)) - { - case Nif::BSShaderTextureSet::TextureType::Base: - texture2d->setName("diffuseMap"); - break; - case Nif::BSShaderTextureSet::TextureType::Normal: - texture2d->setName("normalMap"); - break; - case Nif::BSShaderTextureSet::TextureType::Glow: - texture2d->setName("emissiveMap"); - break; - default: - break; - } - boundTextures.emplace_back(uvSet); } } @@ -2458,11 +2438,12 @@ namespace NifOsg node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->mType))); node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); + clearBoundTextures(stateset, boundTextures); + const bool wrapS = (texprop->mClamp >> 1) & 0x1; + const bool wrapT = texprop->mClamp & 0x1; if (!texprop->mTextureSet.empty()) - { - auto textureSet = texprop->mTextureSet.getPtr(); - handleTextureSet(textureSet, texprop->mClamp, node->getName(), stateset, boundTextures); - } + handleTextureSet( + texprop->mTextureSet.getPtr(), wrapS, wrapT, node->getName(), stateset, boundTextures); handleTextureControllers(texprop, composite, stateset, animflags); if (texprop->refraction()) SceneUtil::setupDistortion(*node, texprop->mRefraction.mStrength); @@ -2476,31 +2457,17 @@ namespace NifOsg node->setUserValue("shaderPrefix", std::string(getBSShaderPrefix(texprop->mType))); node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); + clearBoundTextures(stateset, boundTextures); if (!texprop->mFilename.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(texprop->mFilename, mImageManager->getVFS()); - osg::ref_ptr image = mImageManager->getImage(filename); - osg::ref_ptr texture2d = new osg::Texture2D(image); - texture2d->setName("diffuseMap"); - if (image) - texture2d->setTextureSize(image->s(), image->t()); - handleTextureWrapping(texture2d, texprop->wrapS(), texprop->wrapT()); - const unsigned int texUnit = 0; const unsigned int uvSet = 0; - stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); - boundTextures.push_back(uvSet); - if (mBethVersion >= 27) - { - useFalloff = true; - stateset->addUniform(new osg::Uniform("falloffParams", texprop->mFalloffParams)); - } + attachExternalTexture("diffuseMap", texprop->mFilename, texprop->wrapS(), texprop->wrapT(), + uvSet, stateset, boundTextures); + } + if (mBethVersion >= 27) + { + useFalloff = true; + stateset->addUniform(new osg::Uniform("falloffParams", texprop->mFalloffParams)); } stateset->addUniform(new osg::Uniform("useFalloff", useFalloff)); handleTextureControllers(texprop, composite, stateset, animflags); @@ -2514,15 +2481,17 @@ namespace NifOsg node->setUserValue("shaderPrefix", std::string(getBSLightingShaderPrefix(texprop->mType))); node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); - Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName); - if (material) + clearBoundTextures(stateset, boundTextures); + if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName)) { - handleShaderMaterial(material, stateset, boundTextures); + handleShaderMaterialNodeProperties(material, stateset, boundTextures); break; } + const bool wrapS = (texprop->mClamp >> 1) & 0x1; + const bool wrapT = texprop->mClamp & 0x1; if (!texprop->mTextureSet.empty()) handleTextureSet( - texprop->mTextureSet.getPtr(), texprop->mClamp, node->getName(), stateset, boundTextures); + texprop->mTextureSet.getPtr(), wrapS, wrapT, node->getName(), stateset, boundTextures); handleTextureControllers(texprop, composite, stateset, animflags); if (texprop->doubleSided()) stateset->setMode(GL_CULL_FACE, osg::StateAttribute::OFF); @@ -2541,33 +2510,20 @@ namespace NifOsg node->setUserValue("shaderPrefix", std::string("bs/nolighting")); node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); - Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName); - if (material) + clearBoundTextures(stateset, boundTextures); + if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName)) { - handleShaderMaterial(material, stateset, boundTextures); + handleShaderMaterialNodeProperties(material, stateset, boundTextures); break; } if (!texprop->mSourceTexture.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( - texprop->mSourceTexture, mImageManager->getVFS()); - osg::ref_ptr image = mImageManager->getImage(filename); - osg::ref_ptr texture2d = new osg::Texture2D(image); - texture2d->setName("diffuseMap"); - if (image) - texture2d->setTextureSize(image->s(), image->t()); - handleTextureWrapping(texture2d, (texprop->mClamp >> 1) & 0x1, texprop->mClamp & 0x1); - const unsigned int texUnit = 0; const unsigned int uvSet = 0; - stateset->setTextureAttributeAndModes(texUnit, texture2d, osg::StateAttribute::ON); - boundTextures.push_back(uvSet); - + const bool wrapS = (texprop->mClamp >> 1) & 0x1; + const bool wrapT = texprop->mClamp & 0x1; + unsigned int texUnit = boundTextures.size(); + attachExternalTexture( + "diffuseMap", texprop->mSourceTexture, wrapS, wrapT, uvSet, stateset, boundTextures); { osg::ref_ptr texMat(new osg::TexMat); // This handles 20.2.0.7 UV settings like 4.0.0.2 UV settings (see NifOsg::UVController) @@ -2819,40 +2775,11 @@ namespace NifOsg case Nif::RC_BSLightingShaderProperty: { auto shaderprop = static_cast(property); - Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName); - if (shaderMat) + if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName)) { - mat->setAlpha(osg::Material::FRONT_AND_BACK, shaderMat->mTransparency); - if (shaderMat->mAlphaTest) - { - osg::StateSet* stateset = node->getOrCreateStateSet(); - osg::ref_ptr alphaFunc(new osg::AlphaFunc( - osg::AlphaFunc::GREATER, shaderMat->mAlphaTestThreshold / 255.f)); - alphaFunc = shareAttribute(alphaFunc); - stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); - } - if (shaderMat->mAlphaBlend) - { - osg::StateSet* stateset = node->getOrCreateStateSet(); - osg::ref_ptr blendFunc( - new osg::BlendFunc(getBlendMode(shaderMat->mSourceBlendMode), - getBlendMode(shaderMat->mDestinationBlendMode))); - blendFunc = shareAttribute(blendFunc); - stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); - hasSortAlpha = true; - if (!mPushedSorter) - setBin_Transparent(stateset); - } - if (shaderMat->mDecal) - { - osg::StateSet* stateset = node->getOrCreateStateSet(); - if (!mPushedSorter && !hasSortAlpha) - stateset->setRenderBinDetails(1, "SORT_BACK_TO_FRONT"); - osg::ref_ptr polygonOffset(new osg::PolygonOffset); - polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f); - polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f); - stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); - } + handleShaderMaterialDrawableProperties(shaderMat, mat, *node, hasSortAlpha); + if (shaderMat->mAlphaBlend && !mPushedSorter) + setBin_Transparent(node->getOrCreateStateSet()); if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting) { auto bgsm = static_cast(shaderMat.get()); @@ -2860,8 +2787,6 @@ namespace NifOsg = false; // bgsm->mSpecularEnabled; disabled until it can be implemented properly specStrength = bgsm->mSpecularMult; emissiveMult = bgsm->mEmittanceMult; - mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgsm->mEmittanceColor, 1.f)); - mat->setSpecular(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgsm->mSpecularColor, 1.f)); } break; } @@ -2888,47 +2813,11 @@ namespace NifOsg case Nif::RC_BSEffectShaderProperty: { auto shaderprop = static_cast(property); - Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName); - if (shaderMat) + if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName)) { - mat->setAlpha(osg::Material::FRONT_AND_BACK, shaderMat->mTransparency); - if (shaderMat->mAlphaTest) - { - osg::StateSet* stateset = node->getOrCreateStateSet(); - osg::ref_ptr alphaFunc(new osg::AlphaFunc( - osg::AlphaFunc::GREATER, shaderMat->mAlphaTestThreshold / 255.f)); - alphaFunc = shareAttribute(alphaFunc); - stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); - } - if (shaderMat->mAlphaBlend) - { - osg::StateSet* stateset = node->getOrCreateStateSet(); - osg::ref_ptr blendFunc( - new osg::BlendFunc(getBlendMode(shaderMat->mSourceBlendMode), - getBlendMode(shaderMat->mDestinationBlendMode))); - blendFunc = shareAttribute(blendFunc); - stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); - hasSortAlpha = true; - if (!mPushedSorter) - setBin_Transparent(stateset); - } - if (shaderMat->mDecal) - { - osg::StateSet* stateset = node->getOrCreateStateSet(); - if (!mPushedSorter && !hasSortAlpha) - stateset->setRenderBinDetails(1, "SORT_BACK_TO_FRONT"); - osg::ref_ptr polygonOffset(new osg::PolygonOffset); - polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f); - polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f); - stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); - } - if (shaderMat->mShaderType == Bgsm::ShaderType::Effect) - { - auto bgem = static_cast(shaderMat.get()); - mat->setEmission(osg::Material::FRONT_AND_BACK, osg::Vec4f(bgem->mEmittanceColor, 1.f)); - if (bgem->mSoft) - SceneUtil::setupSoftEffect(*node, bgem->mSoftDepth, true, bgem->mSoftDepth); - } + handleShaderMaterialDrawableProperties(shaderMat, mat, *node, hasSortAlpha); + if (shaderMat->mAlphaBlend && !mPushedSorter) + setBin_Transparent(node->getOrCreateStateSet()); break; } if (shaderprop->decal()) From 5789eb73b104bc93b433528bd0df476b7b843682 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Fri, 19 Apr 2024 16:47:00 +0300 Subject: [PATCH 16/24] Deduplicate decal and alpha handling in NifLoader --- components/nifosg/nifloader.cpp | 160 ++++++++++++++------------------ 1 file changed, 69 insertions(+), 91 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 7177377771..5af4e57c53 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -2209,36 +2209,79 @@ namespace NifOsg handleDepthFlags(stateset, material->mDepthTest, material->mDepthWrite); } - void handleShaderMaterialDrawableProperties( - Bgsm::MaterialFilePtr shaderMat, osg::ref_ptr mat, osg::Node& node, bool& hasSortAlpha) + void handleDecal(bool hasSortAlpha, osg::ref_ptr stateset) { - mat->setAlpha(osg::Material::FRONT_AND_BACK, shaderMat->mTransparency); - if (shaderMat->mAlphaTest) + osg::ref_ptr polygonOffset(new osg::PolygonOffset); + polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f); + polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f); + polygonOffset = shareAttribute(polygonOffset); + stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); + if (!mPushedSorter && !hasSortAlpha) + stateset->setRenderBinDetails(1, "SORT_BACK_TO_FRONT"); + } + + void handleAlphaTesting( + bool enabled, osg::AlphaFunc::ComparisonFunction function, int threshold, osg::Node& node) + { + if (enabled) { - osg::StateSet* stateset = node.getOrCreateStateSet(); - osg::ref_ptr alphaFunc( - new osg::AlphaFunc(osg::AlphaFunc::GREATER, shaderMat->mAlphaTestThreshold / 255.f)); + osg::ref_ptr alphaFunc(new osg::AlphaFunc(function, threshold / 255.f)); alphaFunc = shareAttribute(alphaFunc); - stateset->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); + node.getOrCreateStateSet()->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); } - if (shaderMat->mAlphaBlend) + else if (osg::StateSet* stateset = node.getStateSet()) { - osg::StateSet* stateset = node.getOrCreateStateSet(); - osg::ref_ptr blendFunc(new osg::BlendFunc( - getBlendMode(shaderMat->mSourceBlendMode), getBlendMode(shaderMat->mDestinationBlendMode))); + stateset->removeAttribute(osg::StateAttribute::ALPHAFUNC); + stateset->removeMode(GL_ALPHA_TEST); + } + } + + void handleAlphaBlending( + bool enabled, int sourceMode, int destMode, bool sort, bool& hasSortAlpha, osg::Node& node) + { + if (enabled) + { + osg::ref_ptr stateset = node.getOrCreateStateSet(); + osg::ref_ptr blendFunc( + new osg::BlendFunc(getBlendMode(sourceMode), getBlendMode(destMode))); + // on AMD hardware, alpha still seems to be stored with an RGBA framebuffer with OpenGL. + // This might be mandated by the OpenGL 2.1 specification section 2.14.9, or might be a bug. + // Either way, D3D8.1 doesn't do that, so adapt the destination factor. + if (blendFunc->getDestination() == GL_DST_ALPHA) + blendFunc->setDestination(GL_ONE); blendFunc = shareAttribute(blendFunc); stateset->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); - hasSortAlpha = true; + + if (sort) + { + hasSortAlpha = true; + if (!mPushedSorter) + stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); + } + else if (!mPushedSorter) + { + stateset->setRenderBinToInherit(); + } } + else if (osg::ref_ptr stateset = node.getStateSet()) + { + stateset->removeAttribute(osg::StateAttribute::BLENDFUNC); + stateset->removeMode(GL_BLEND); + if (!mPushedSorter) + stateset->setRenderBinToInherit(); + } + } + + void handleShaderMaterialDrawableProperties( + Bgsm::MaterialFilePtr shaderMat, osg::ref_ptr mat, osg::Node& node, bool& hasSortAlpha) + { + mat->setAlpha(osg::Material::FRONT_AND_BACK, shaderMat->mTransparency); + handleAlphaTesting(shaderMat->mAlphaTest, osg::AlphaFunc::GREATER, shaderMat->mAlphaTestThreshold, node); + handleAlphaBlending(shaderMat->mAlphaBlend, shaderMat->mSourceBlendMode, shaderMat->mDestinationBlendMode, + true, hasSortAlpha, node); if (shaderMat->mDecal) { - osg::StateSet* stateset = node.getOrCreateStateSet(); - if (!mPushedSorter && !hasSortAlpha) - stateset->setRenderBinDetails(1, "SORT_BACK_TO_FRONT"); - osg::ref_ptr polygonOffset(new osg::PolygonOffset); - polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f); - polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f); - stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); + handleDecal(hasSortAlpha, node.getOrCreateStateSet()); } if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting) { @@ -2627,12 +2670,9 @@ namespace NifOsg bool hasMatCtrl = false; bool hasSortAlpha = false; - osg::StateSet* blendFuncStateSet = nullptr; - auto setBin_Transparent = [](osg::StateSet* ss) { ss->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); }; auto setBin_BackToFront = [](osg::StateSet* ss) { ss->setRenderBinDetails(0, "SORT_BACK_TO_FRONT"); }; auto setBin_Traversal = [](osg::StateSet* ss) { ss->setRenderBinDetails(2, "TraversalOrderBin"); }; - auto setBin_Inherit = [](osg::StateSet* ss) { ss->setRenderBinToInherit(); }; auto lightmode = Nif::NiVertexColorProperty::LightMode::LightMode_EmiAmbDif; float emissiveMult = 1.f; @@ -2718,52 +2758,10 @@ namespace NifOsg case Nif::RC_NiAlphaProperty: { const Nif::NiAlphaProperty* alphaprop = static_cast(property); - if (alphaprop->useAlphaBlending()) - { - osg::ref_ptr blendFunc( - new osg::BlendFunc(getBlendMode(alphaprop->sourceBlendMode()), - getBlendMode(alphaprop->destinationBlendMode()))); - // on AMD hardware, alpha still seems to be stored with an RGBA framebuffer with OpenGL. - // This might be mandated by the OpenGL 2.1 specification section 2.14.9, or might be a bug. - // Either way, D3D8.1 doesn't do that, so adapt the destination factor. - if (blendFunc->getDestination() == GL_DST_ALPHA) - blendFunc->setDestination(GL_ONE); - blendFunc = shareAttribute(blendFunc); - node->getOrCreateStateSet()->setAttributeAndModes(blendFunc, osg::StateAttribute::ON); - - if (!alphaprop->noSorter()) - { - hasSortAlpha = true; - if (!mPushedSorter) - setBin_Transparent(node->getStateSet()); - } - else - { - if (!mPushedSorter) - setBin_Inherit(node->getStateSet()); - } - } - else if (osg::StateSet* stateset = node->getStateSet()) - { - stateset->removeAttribute(osg::StateAttribute::BLENDFUNC); - stateset->removeMode(GL_BLEND); - blendFuncStateSet = stateset; - if (!mPushedSorter) - blendFuncStateSet->setRenderBinToInherit(); - } - - if (alphaprop->useAlphaTesting()) - { - osg::ref_ptr alphaFunc(new osg::AlphaFunc( - getTestMode(alphaprop->alphaTestMode()), alphaprop->mThreshold / 255.f)); - alphaFunc = shareAttribute(alphaFunc); - node->getOrCreateStateSet()->setAttributeAndModes(alphaFunc, osg::StateAttribute::ON); - } - else if (osg::StateSet* stateset = node->getStateSet()) - { - stateset->removeAttribute(osg::StateAttribute::ALPHAFUNC); - stateset->removeMode(GL_ALPHA_TEST); - } + handleAlphaBlending(alphaprop->useAlphaBlending(), alphaprop->sourceBlendMode(), + alphaprop->destinationBlendMode(), !alphaprop->noSorter(), hasSortAlpha, *node); + handleAlphaTesting(alphaprop->useAlphaTesting(), getTestMode(alphaprop->alphaTestMode()), + alphaprop->mThreshold, *node); break; } case Nif::RC_BSShaderPPLightingProperty: @@ -2778,8 +2776,6 @@ namespace NifOsg if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName)) { handleShaderMaterialDrawableProperties(shaderMat, mat, *node, hasSortAlpha); - if (shaderMat->mAlphaBlend && !mPushedSorter) - setBin_Transparent(node->getOrCreateStateSet()); if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting) { auto bgsm = static_cast(shaderMat.get()); @@ -2799,15 +2795,7 @@ namespace NifOsg specStrength = shaderprop->mSpecStrength; specEnabled = shaderprop->specular(); if (shaderprop->decal()) - { - osg::StateSet* stateset = node->getOrCreateStateSet(); - if (!mPushedSorter && !hasSortAlpha) - stateset->setRenderBinDetails(1, "SORT_BACK_TO_FRONT"); - osg::ref_ptr polygonOffset(new osg::PolygonOffset); - polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f); - polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f); - stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); - } + handleDecal(hasSortAlpha, node->getOrCreateStateSet()); break; } case Nif::RC_BSEffectShaderProperty: @@ -2816,20 +2804,10 @@ namespace NifOsg if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName)) { handleShaderMaterialDrawableProperties(shaderMat, mat, *node, hasSortAlpha); - if (shaderMat->mAlphaBlend && !mPushedSorter) - setBin_Transparent(node->getOrCreateStateSet()); break; } if (shaderprop->decal()) - { - osg::StateSet* stateset = node->getOrCreateStateSet(); - if (!mPushedSorter && !hasSortAlpha) - stateset->setRenderBinDetails(1, "SORT_BACK_TO_FRONT"); - osg::ref_ptr polygonOffset(new osg::PolygonOffset); - polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f); - polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f); - stateset->setAttributeAndModes(polygonOffset, osg::StateAttribute::ON); - } + handleDecal(hasSortAlpha, node->getOrCreateStateSet()); if (shaderprop->softEffect()) SceneUtil::setupSoftEffect( *node, shaderprop->mFalloffDepth, true, shaderprop->mFalloffDepth); From 4e3d45db1baf2f656ac1f26cee1d8d31e7c74678 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 20 Apr 2024 07:46:41 +0300 Subject: [PATCH 17/24] Deduplicate file handling in niftest --- apps/niftest/niftest.cpp | 79 ++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 52 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index c364303376..8f8c408a87 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -26,7 +26,7 @@ namespace bpo = boost::program_options; /// See if the file has the named extension -bool hasExtension(const std::filesystem::path& filename, const std::string& extensionToFind) +bool hasExtension(const std::filesystem::path& filename, std::string_view extensionToFind) { const auto extension = Files::pathToUnicodeString(filename.extension()); return Misc::StringUtils::ciEqual(extension, extensionToFind); @@ -59,16 +59,17 @@ std::unique_ptr makeArchive(const std::filesystem::path& path) return nullptr; } -void readNIF( +void readFile( const std::filesystem::path& source, const std::filesystem::path& path, const VFS::Manager* vfs, bool quiet) { const std::string pathStr = Files::pathToUnicodeString(path); + const bool isNif = isNIF(path); if (!quiet) { - if (hasExtension(path, ".kf")) - std::cout << "Reading KF file '" << pathStr << "'"; + if (isNif) + std::cout << "Reading " << (hasExtension(path, ".nif") ? "NIF" : "KF") << " file '" << pathStr << "'"; else - std::cout << "Reading NIF file '" << pathStr << "'"; + std::cout << "Reading " << (hasExtension(path, ".bgsm") ? "BGSM" : "BGEM") << " file '" << pathStr << "'"; if (!source.empty()) std::cout << " from '" << Files::pathToUnicodeString(isBSA(source) ? source.filename() : source) << "'"; std::cout << std::endl; @@ -76,41 +77,23 @@ void readNIF( const std::filesystem::path fullPath = !source.empty() ? source / path : path; try { - Nif::NIFFile file(Files::pathToUnicodeString(fullPath)); - Nif::Reader reader(file, nullptr); - if (vfs != nullptr) - reader.parse(vfs->get(pathStr)); - else - reader.parse(Files::openConstrainedFileStream(fullPath)); - } - catch (std::exception& e) - { - std::cerr << "Failed to read '" << pathStr << "':" << std::endl << e.what() << std::endl; - } -} - -void readMaterial( - const std::filesystem::path& source, const std::filesystem::path& path, const VFS::Manager* vfs, bool quiet) -{ - const std::string pathStr = Files::pathToUnicodeString(path); - if (!quiet) - { - if (hasExtension(path, ".bgem")) - std::cout << "Reading BGEM file '" << pathStr << "'"; - else - std::cout << "Reading BGSM file '" << pathStr << "'"; - if (!source.empty()) - std::cout << " from '" << Files::pathToUnicodeString(isBSA(source) ? source.filename() : source) << "'"; - std::cout << std::endl; - } - const std::filesystem::path fullPath = !source.empty() ? source / path : path; - try - { - Bgsm::Reader reader; - if (vfs != nullptr) - reader.parse(vfs->get(pathStr)); + if (isNif) + { + Nif::NIFFile file(Files::pathToUnicodeString(fullPath)); + Nif::Reader reader(file, nullptr); + if (vfs != nullptr) + reader.parse(vfs->get(pathStr)); + else + reader.parse(Files::openConstrainedFileStream(fullPath)); + } else - reader.parse(Files::openConstrainedFileStream(fullPath)); + { + Bgsm::Reader reader; + if (vfs != nullptr) + reader.parse(vfs->get(pathStr)); + else + reader.parse(Files::openConstrainedFileStream(fullPath)); + } } catch (std::exception& e) { @@ -134,13 +117,9 @@ void readVFS(std::unique_ptr&& archive, const std::filesystem::pat for (const auto& name : vfs.getRecursiveDirectoryIterator("")) { - if (isNIF(name.value())) + if (isNIF(name.value()) || isMaterial(name.value())) { - readNIF(archivePath, name.value(), &vfs, quiet); - } - else if (isMaterial(name.value())) - { - readMaterial(archivePath, name.value(), &vfs, quiet); + readFile(archivePath, name.value(), &vfs, quiet); } } @@ -262,13 +241,9 @@ int main(int argc, char** argv) const std::string pathStr = Files::pathToUnicodeString(path); try { - if (isNIF(path)) - { - readNIF({}, path, vfs.get(), quiet); - } - else if (isMaterial(path)) + if (isNIF(path) || isMaterial(path)) { - readMaterial({}, path, vfs.get(), quiet); + readFile({}, path, vfs.get(), quiet); } else if (auto archive = makeArchive(path)) { @@ -276,7 +251,7 @@ int main(int argc, char** argv) } else { - std::cerr << "Error: '" << pathStr << "' is not a NIF/KF file, BSA/BA2 archive, or directory" + std::cerr << "Error: '" << pathStr << "' is not a NIF/KF/BGEM/BGSM file, BSA/BA2 archive, or directory" << std::endl; } } From 6f9206428d882a176952c6fa8bc3014a9c614bdc Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 20 Apr 2024 07:53:16 +0300 Subject: [PATCH 18/24] Don't ignore material files in NifLoader tests --- apps/openmw_test_suite/nifosg/testnifloader.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw_test_suite/nifosg/testnifloader.cpp b/apps/openmw_test_suite/nifosg/testnifloader.cpp index cdab51e6c2..fa023fff0d 100644 --- a/apps/openmw_test_suite/nifosg/testnifloader.cpp +++ b/apps/openmw_test_suite/nifosg/testnifloader.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,7 @@ namespace { VFS::Manager mVfs; Resource::ImageManager mImageManager{ &mVfs, 0 }; + Resource::BgsmFileManager mMaterialManager{ &mVfs, 0 }; const osgDB::ReaderWriter* mReaderWriter = osgDB::Registry::instance()->getReaderWriterForExtension("osgt"); osg::ref_ptr mOptions = new osgDB::Options; @@ -70,7 +72,7 @@ namespace init(node); Nif::NIFFile file("test.nif"); file.mRoots.push_back(&node); - auto result = Loader::load(file, &mImageManager, nullptr); + auto result = Loader::load(file, &mImageManager, &mMaterialManager); EXPECT_EQ(serialize(*result), R"( osg::Group { UniqueID 1 @@ -259,7 +261,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, nullptr); + auto result = Loader::load(file, &mImageManager, &mMaterialManager); EXPECT_EQ(serialize(*result), formatOsgNodeForBSShaderProperty(GetParam().mExpectedShaderPrefix)); } @@ -289,7 +291,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, nullptr); + auto result = Loader::load(file, &mImageManager, &mMaterialManager); EXPECT_EQ(serialize(*result), formatOsgNodeForBSLightingShaderProperty(GetParam().mExpectedShaderPrefix)); } From 32d24e73fedd482f531d285b3daa211ebcce7131 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 20 Apr 2024 08:00:27 +0300 Subject: [PATCH 19/24] Update changelog (#7777, Bethesda material files) --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35cf145f82..caa3299c97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -219,6 +219,7 @@ Feature #7652: Sort inactive post processing shaders list properly Feature #7698: Implement sAbsorb, sDamage, sDrain, sFortify and sRestore Feature #7709: Improve resolution selection in Launcher + Feature #7777: Support external Bethesda material files (BGSM/BGEM) Feature #7792: Support Timescale Clouds Feature #7795: Support MaxNumberRipples INI setting Feature #7805: Lua Menu context From 8325e100df61854a29494daf815b9be14a65820e Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 20 Apr 2024 08:12:53 +0300 Subject: [PATCH 20/24] More decal deduplication --- components/nifosg/nifloader.cpp | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 5af4e57c53..4616ac440f 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -2209,8 +2209,11 @@ namespace NifOsg handleDepthFlags(stateset, material->mDepthTest, material->mDepthWrite); } - void handleDecal(bool hasSortAlpha, osg::ref_ptr stateset) + void handleDecal(bool enabled, bool hasSortAlpha, osg::Node& node) { + if (!enabled) + return; + osg::ref_ptr stateset = node.getOrCreateStateSet(); osg::ref_ptr polygonOffset(new osg::PolygonOffset); polygonOffset->setUnits(SceneUtil::AutoDepth::isReversed() ? 1.f : -1.f); polygonOffset->setFactor(SceneUtil::AutoDepth::isReversed() ? 0.65f : -0.65f); @@ -2279,10 +2282,7 @@ namespace NifOsg handleAlphaTesting(shaderMat->mAlphaTest, osg::AlphaFunc::GREATER, shaderMat->mAlphaTestThreshold, node); handleAlphaBlending(shaderMat->mAlphaBlend, shaderMat->mSourceBlendMode, shaderMat->mDestinationBlendMode, true, hasSortAlpha, node); - if (shaderMat->mDecal) - { - handleDecal(hasSortAlpha, node.getOrCreateStateSet()); - } + handleDecal(shaderMat->mDecal, hasSortAlpha, node); if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting) { auto bgsm = static_cast(shaderMat.get()); @@ -2794,8 +2794,7 @@ namespace NifOsg emissiveMult = shaderprop->mEmissiveMult; specStrength = shaderprop->mSpecStrength; specEnabled = shaderprop->specular(); - if (shaderprop->decal()) - handleDecal(hasSortAlpha, node->getOrCreateStateSet()); + handleDecal(shaderprop->decal(), hasSortAlpha, *node); break; } case Nif::RC_BSEffectShaderProperty: @@ -2806,8 +2805,7 @@ namespace NifOsg handleShaderMaterialDrawableProperties(shaderMat, mat, *node, hasSortAlpha); break; } - if (shaderprop->decal()) - handleDecal(hasSortAlpha, node->getOrCreateStateSet()); + handleDecal(shaderprop->decal(), hasSortAlpha, *node); if (shaderprop->softEffect()) SceneUtil::setupSoftEffect( *node, shaderprop->mFalloffDepth, true, shaderprop->mFalloffDepth); From ea5e101821debafb63f47d263c507ce0b4bc4248 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 20 Apr 2024 08:24:50 +0300 Subject: [PATCH 21/24] Handle glow maps for BGSM files --- components/nifosg/nifloader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 4616ac440f..f661e1e8c6 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -2188,6 +2188,9 @@ namespace NifOsg if (!bgsm->mNormalMap.empty()) attachExternalTexture("normalMap", bgsm->mNormalMap, wrapS, wrapT, uvSet, stateset, boundTextures); + if (bgsm->mGlowMapEnabled && !bgsm->mGlowMap.empty()) + attachExternalTexture("emissiveMap", bgsm->mGlowMap, wrapS, wrapT, uvSet, stateset, boundTextures); + if (bgsm->mTree) stateset->addUniform(new osg::Uniform("useTreeAnim", true)); } From 6be2bb70c3cbf478b21d40405e031bfada9e57e4 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 20 Apr 2024 18:11:56 +0300 Subject: [PATCH 22/24] Remove unused remnants of NIFStream from BGSMStream --- components/bgsm/reader.cpp | 2 +- components/bgsm/reader.hpp | 2 - components/bgsm/stream.cpp | 96 +++++--------------------------------- components/bgsm/stream.hpp | 73 ++--------------------------- 4 files changed, 17 insertions(+), 156 deletions(-) diff --git a/components/bgsm/reader.cpp b/components/bgsm/reader.cpp index c89d872bd7..47c052fb81 100644 --- a/components/bgsm/reader.cpp +++ b/components/bgsm/reader.cpp @@ -11,7 +11,7 @@ namespace Bgsm { void Reader::parse(Files::IStreamPtr&& inputStream) { - BGSMStream stream(*this, std::move(inputStream)); + BGSMStream stream(std::move(inputStream)); std::array signature; stream.readArray(signature); diff --git a/components/bgsm/reader.hpp b/components/bgsm/reader.hpp index 2d669900ad..2d8a0ed481 100644 --- a/components/bgsm/reader.hpp +++ b/components/bgsm/reader.hpp @@ -19,8 +19,6 @@ namespace Bgsm public: void parse(Files::IStreamPtr&& stream); - std::uint32_t getVersion() const { return mFile->mVersion; } - std::unique_ptr& getFile() { return mFile; } }; } diff --git a/components/bgsm/stream.cpp b/components/bgsm/stream.cpp index 00cc382d3f..2c11066709 100644 --- a/components/bgsm/stream.cpp +++ b/components/bgsm/stream.cpp @@ -1,66 +1,8 @@ #include "stream.hpp" -#include - -#include "reader.hpp" - -namespace -{ - - // Read a range of elements into a dynamic buffer per-element - // This one should be used if the type cannot be read contiguously - // (e.g. quaternions) - template - void readRange(Bgsm::BGSMStream& stream, T* dest, size_t size) - { - for (T& value : std::span(dest, size)) - stream.read(value); - } - - // Read a range of elements into a dynamic buffer - // This one should be used if the type can be read contiguously as an array of a different type - // (e.g. osg::VecXf can be read as a float array of X elements) - template - void readAlignedRange(Files::IStreamPtr& stream, T* dest, size_t size) - { - static_assert(std::is_standard_layout_v); - static_assert(std::alignment_of_v == std::alignment_of_v); - static_assert(sizeof(T) == sizeof(elementType) * numElements); - Bgsm::readDynamicBufferOfType(stream, reinterpret_cast(dest), size * numElements); - } - -} - namespace Bgsm { - std::uint32_t BGSMStream::getVersion() const - { - return mReader.getVersion(); - } - - std::string BGSMStream::getSizedString(size_t length) - { - // Prevent potential memory allocation freezes; strings this long are not expected in BGSM - if (length > 1024) - throw std::runtime_error("Requested string length is too large: " + std::to_string(length)); - std::string str(length, '\0'); - mStream->read(str.data(), length); - if (mStream->bad()) - throw std::runtime_error("Failed to read sized string of " + std::to_string(length) + " chars"); - size_t end = str.find('\0'); - if (end != std::string::npos) - str.erase(end); - return str; - } - - void BGSMStream::getSizedStrings(std::vector& vec, size_t size) - { - vec.resize(size); - for (size_t i = 0; i < vec.size(); i++) - vec[i] = getSizedString(); - } - template <> void BGSMStream::read(osg::Vec2f& vec) { @@ -82,32 +24,18 @@ namespace Bgsm template <> void BGSMStream::read(std::string& str) { - str = getSizedString(); - } - - template <> - void BGSMStream::read(osg::Vec2f* dest, size_t size) - { - readAlignedRange(mStream, dest, size); - } - - template <> - void BGSMStream::read(osg::Vec3f* dest, size_t size) - { - readAlignedRange(mStream, dest, size); - } - - template <> - void BGSMStream::read(osg::Vec4f* dest, size_t size) - { - readAlignedRange(mStream, dest, size); - } - - template <> - void BGSMStream::read(std::string* dest, size_t size) - { - for (std::string& value : std::span(dest, size)) - value = getSizedString(); + std::uint32_t length; + read(length); + // Prevent potential memory allocation freezes; strings this long are not expected in BGSM + if (length > 1024) + throw std::runtime_error("Requested string length is too large: " + std::to_string(length)); + str = std::string(length, '\0'); + mStream->read(str.data(), length); + if (mStream->bad()) + throw std::runtime_error("Failed to read sized string of " + std::to_string(length) + " chars"); + std::size_t end = str.find('\0'); + if (end != std::string::npos) + str.erase(end); } } diff --git a/components/bgsm/stream.hpp b/components/bgsm/stream.hpp index 2e03a52dd4..a355523367 100644 --- a/components/bgsm/stream.hpp +++ b/components/bgsm/stream.hpp @@ -8,23 +8,21 @@ #include #include #include -#include #include #include +#include #include #include namespace Bgsm { - class Reader; - template inline void readBufferOfType(Files::IStreamPtr& pIStream, T* dest) { static_assert(std::is_arithmetic_v, "Buffer element type is not arithmetic"); - pIStream->read((char*)dest, numInstances * sizeof(T)); + pIStream->read(reinterpret_cast(dest), numInstances * sizeof(T)); if (pIStream->bad()) throw std::runtime_error("Failed to read typed (" + std::string(typeid(T).name()) + ") buffer of " + std::to_string(numInstances) + " instances"); @@ -39,35 +37,16 @@ namespace Bgsm readBufferOfType(pIStream, static_cast(dest)); } - template - inline void readDynamicBufferOfType(Files::IStreamPtr& pIStream, T* dest, std::size_t numInstances) - { - static_assert(std::is_arithmetic_v, "Buffer element type is not arithmetic"); - pIStream->read((char*)dest, numInstances * sizeof(T)); - if (pIStream->bad()) - throw std::runtime_error("Failed to read typed (" + std::string(typeid(T).name()) + ") dynamic buffer of " - + std::to_string(numInstances) + " instances"); - if constexpr (Misc::IS_BIG_ENDIAN) - for (std::size_t i = 0; i < numInstances; i++) - Misc::swapEndiannessInplace(dest[i]); - } - class BGSMStream { - const Reader& mReader; Files::IStreamPtr mStream; public: - explicit BGSMStream(const Reader& reader, Files::IStreamPtr&& stream) - : mReader(reader) - , mStream(std::move(stream)) + explicit BGSMStream(Files::IStreamPtr&& stream) + : mStream(std::move(stream)) { } - const Reader& getFile() const { return mReader; } - - std::uint32_t getVersion() const; - void skip(size_t size) { mStream->ignore(size); } /// Read into a single instance of type @@ -83,41 +62,6 @@ namespace Bgsm { readBufferOfType(mStream, arr.data()); } - - /// Read instances of type into a dynamic buffer - template - void read(T* dest, size_t size) - { - readDynamicBufferOfType(mStream, dest, size); - } - - /// Read multiple instances of type into a vector - template - void readVector(std::vector& vec, size_t size) - { - if (size == 0) - return; - vec.resize(size); - read(vec.data(), size); - } - - /// Extract an instance of type - template - T get() - { - T data; - read(data); - return data; - } - - /// Read a string of the given length - std::string getSizedString(size_t length); - - /// Read a string of the length specified in the file - std::string getSizedString() { return getSizedString(get()); } - - /// Read a list of strings - void getSizedStrings(std::vector& vec, size_t size); }; template <> @@ -128,15 +72,6 @@ namespace Bgsm void BGSMStream::read(osg::Vec4f& vec); template <> void BGSMStream::read(std::string& str); - - template <> - void BGSMStream::read(osg::Vec2f* dest, size_t size); - template <> - void BGSMStream::read(osg::Vec3f* dest, size_t size); - template <> - void BGSMStream::read(osg::Vec4f* dest, size_t size); - template <> - void BGSMStream::read(std::string* dest, size_t size); } #endif From 77c3cd44674eb9e875e3a87f6f672bdf1e16bf7d Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 20 Apr 2024 18:48:12 +0300 Subject: [PATCH 23/24] More BGSM cleanup --- components/bgsm/file.cpp | 40 ++++++++----------------- components/bgsm/file.hpp | 2 +- components/bgsm/reader.cpp | 1 - components/bgsm/stream.cpp | 2 -- components/nifosg/nifloader.cpp | 22 +++++++------- components/resource/bgsmfilemanager.cpp | 4 --- components/resource/bgsmfilemanager.hpp | 2 +- 7 files changed, 25 insertions(+), 48 deletions(-) diff --git a/components/bgsm/file.cpp b/components/bgsm/file.cpp index f330d8a84e..6b763321be 100644 --- a/components/bgsm/file.cpp +++ b/components/bgsm/file.cpp @@ -38,9 +38,7 @@ namespace Bgsm } stream.read(mGrayscaleToPaletteColor); if (mVersion >= 6) - { stream.read(mMaskWrites); - } } void BGSMFile::read(BGSMStream& stream) @@ -59,9 +57,7 @@ namespace Bgsm stream.read(mLightingMap); stream.read(mFlowMap); if (mVersion >= 17) - { stream.read(mDistanceFieldAlphaMap); - } } else { @@ -98,9 +94,7 @@ namespace Bgsm stream.read(mWetnessControlSpecPowerScale); stream.read(mWetnessControlSpecMinvar); if (mVersion < 10) - { stream.read(mWetnessControlEnvMapScale); - } stream.read(mWetnessControlFresnelPower); stream.read(mWetnessControlMetalness); if (mVersion >= 3) @@ -116,9 +110,7 @@ namespace Bgsm stream.read(mAnisoLighting); stream.read(mEmitEnabled); if (mEmitEnabled) - { stream.read(mEmittanceColor); - } stream.read(mEmittanceMult); stream.read(mModelSpaceNormals); stream.read(mExternalEmittance); @@ -181,14 +173,14 @@ namespace Bgsm stream.read(mEnvMap); stream.read(mNormalMap); stream.read(mEnvMapMask); + if (mVersion >= 11) + { + stream.read(mSpecularMap); + stream.read(mLightingMap); + stream.read(mGlowMap); + } if (mVersion >= 10) { - if (mVersion >= 11) - { - stream.read(mSpecularMap); - stream.read(mLightingMap); - stream.read(mGlowMap); - } stream.read(mEnvMapEnabled); stream.read(mEnvMapMaskScale); } @@ -205,20 +197,12 @@ namespace Bgsm stream.read(mEnvmapMinLOD); stream.read(mSoftDepth); if (mVersion >= 11) - { stream.read(mEmittanceColor); - if (mVersion >= 15) - { - stream.read(mAdaptiveEmissiveExposureParams); - if (mVersion >= 16) - { - stream.read(mGlowMapEnabled); - if (mVersion >= 20) - { - stream.read(mEffectPbrSpecular); - } - } - } - } + if (mVersion >= 15) + stream.read(mAdaptiveEmissiveExposureParams); + if (mVersion >= 16) + stream.read(mGlowMapEnabled); + if (mVersion >= 20) + stream.read(mEffectPbrSpecular); } } diff --git a/components/bgsm/file.hpp b/components/bgsm/file.hpp index 3524297e2d..d3fb189256 100644 --- a/components/bgsm/file.hpp +++ b/components/bgsm/file.hpp @@ -50,7 +50,7 @@ namespace Bgsm MaterialFile() = default; virtual void read(BGSMStream& stream); - virtual ~MaterialFile() {} + virtual ~MaterialFile() = default; }; struct BGSMFile : MaterialFile diff --git a/components/bgsm/reader.cpp b/components/bgsm/reader.cpp index 47c052fb81..facdee9fb2 100644 --- a/components/bgsm/reader.cpp +++ b/components/bgsm/reader.cpp @@ -31,5 +31,4 @@ namespace Bgsm mFile->read(stream); } - } diff --git a/components/bgsm/stream.cpp b/components/bgsm/stream.cpp index 2c11066709..c4fa9c1d8c 100644 --- a/components/bgsm/stream.cpp +++ b/components/bgsm/stream.cpp @@ -2,7 +2,6 @@ namespace Bgsm { - template <> void BGSMStream::read(osg::Vec2f& vec) { @@ -37,5 +36,4 @@ namespace Bgsm if (end != std::string::npos) str.erase(end); } - } diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index f661e1e8c6..05a8378c11 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -2151,18 +2151,19 @@ namespace NifOsg handleTextureControllers(texprop, composite, stateset, animflags); } - Bgsm::MaterialFilePtr getShaderMaterial(std::string_view path) const + static Bgsm::MaterialFilePtr getShaderMaterial( + std::string_view path, Resource::BgsmFileManager* materialManager) { - if (!mMaterialManager) + if (!materialManager) return nullptr; if (!Misc::StringUtils::ciEndsWith(path, ".bgem") && !Misc::StringUtils::ciEndsWith(path, ".bgsm")) return nullptr; - std::string normalizedPath = Misc::ResourceHelpers::correctMaterialPath(path, mMaterialManager->getVFS()); + std::string normalizedPath = Misc::ResourceHelpers::correctMaterialPath(path, materialManager->getVFS()); try { - return mMaterialManager->get(VFS::Path::Normalized(normalizedPath)); + return materialManager->get(VFS::Path::Normalized(normalizedPath)); } catch (std::exception& e) { @@ -2528,7 +2529,7 @@ namespace NifOsg node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); clearBoundTextures(stateset, boundTextures); - if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName)) + if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName, mMaterialManager)) { handleShaderMaterialNodeProperties(material, stateset, boundTextures); break; @@ -2557,7 +2558,7 @@ namespace NifOsg node->setUserValue("shaderRequired", shaderRequired); osg::StateSet* stateset = node->getOrCreateStateSet(); clearBoundTextures(stateset, boundTextures); - if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName)) + if (Bgsm::MaterialFilePtr material = getShaderMaterial(texprop->mName, mMaterialManager)) { handleShaderMaterialNodeProperties(material, stateset, boundTextures); break; @@ -2776,15 +2777,14 @@ namespace NifOsg case Nif::RC_BSLightingShaderProperty: { auto shaderprop = static_cast(property); - if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName)) + if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName, mMaterialManager)) { handleShaderMaterialDrawableProperties(shaderMat, mat, *node, hasSortAlpha); if (shaderMat->mShaderType == Bgsm::ShaderType::Lighting) { auto bgsm = static_cast(shaderMat.get()); - specEnabled - = false; // bgsm->mSpecularEnabled; disabled until it can be implemented properly - specStrength = bgsm->mSpecularMult; + specEnabled = false; // bgsm->mSpecularEnabled; TODO: PBR specular lighting + specStrength = 1.f; // bgsm->mSpecularMult; emissiveMult = bgsm->mEmittanceMult; } break; @@ -2803,7 +2803,7 @@ namespace NifOsg case Nif::RC_BSEffectShaderProperty: { auto shaderprop = static_cast(property); - if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName)) + if (Bgsm::MaterialFilePtr shaderMat = getShaderMaterial(shaderprop->mName, mMaterialManager)) { handleShaderMaterialDrawableProperties(shaderMat, mat, *node, hasSortAlpha); break; diff --git a/components/resource/bgsmfilemanager.cpp b/components/resource/bgsmfilemanager.cpp index 5155db17ce..7f749e9453 100644 --- a/components/resource/bgsmfilemanager.cpp +++ b/components/resource/bgsmfilemanager.cpp @@ -1,7 +1,5 @@ #include "bgsmfilemanager.hpp" -#include - #include #include @@ -36,8 +34,6 @@ namespace Resource { } - BgsmFileManager::~BgsmFileManager() = default; - Bgsm::MaterialFilePtr BgsmFileManager::get(VFS::Path::NormalizedView name) { osg::ref_ptr obj = mCache->getRefFromObjectCache(name); diff --git a/components/resource/bgsmfilemanager.hpp b/components/resource/bgsmfilemanager.hpp index b7c0d07c5a..3c77c2c665 100644 --- a/components/resource/bgsmfilemanager.hpp +++ b/components/resource/bgsmfilemanager.hpp @@ -14,7 +14,7 @@ namespace Resource { public: BgsmFileManager(const VFS::Manager* vfs, double expiryDelay); - ~BgsmFileManager(); + ~BgsmFileManager() = default; /// Retrieve a material file from the cache or load it from the VFS if not cached yet. Bgsm::MaterialFilePtr get(VFS::Path::NormalizedView name); From 884668927f73d7c19a9c84793c236620f4f21ab5 Mon Sep 17 00:00:00 2001 From: Alexei Kotov Date: Sat, 20 Apr 2024 21:20:30 +0300 Subject: [PATCH 24/24] BGSM Reader: include cleanup, adjust getFile return result --- components/bgsm/reader.cpp | 1 - components/bgsm/reader.hpp | 5 +---- components/resource/bgsmfilemanager.cpp | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/components/bgsm/reader.cpp b/components/bgsm/reader.cpp index facdee9fb2..eefc8b48b5 100644 --- a/components/bgsm/reader.cpp +++ b/components/bgsm/reader.cpp @@ -4,7 +4,6 @@ #include #include -#include "file.hpp" #include "stream.hpp" namespace Bgsm diff --git a/components/bgsm/reader.hpp b/components/bgsm/reader.hpp index 2d8a0ed481..48508c9143 100644 --- a/components/bgsm/reader.hpp +++ b/components/bgsm/reader.hpp @@ -1,10 +1,7 @@ #ifndef OPENMW_COMPONENTS_BGSM_READER_HPP #define OPENMW_COMPONENTS_BGSM_READER_HPP -#include -#include #include -#include #include @@ -19,7 +16,7 @@ namespace Bgsm public: void parse(Files::IStreamPtr&& stream); - std::unique_ptr& getFile() { return mFile; } + std::unique_ptr getFile() { return std::move(mFile); } }; } #endif diff --git a/components/resource/bgsmfilemanager.cpp b/components/resource/bgsmfilemanager.cpp index 7f749e9453..2d439ccc8a 100644 --- a/components/resource/bgsmfilemanager.cpp +++ b/components/resource/bgsmfilemanager.cpp @@ -43,7 +43,7 @@ namespace Resource { Bgsm::Reader reader; reader.parse(mVFS->get(name)); - Bgsm::MaterialFilePtr file = std::move(reader.getFile()); + Bgsm::MaterialFilePtr file = reader.getFile(); obj = new BgsmFileHolder(file); mCache->addEntryToObjectCache(name.value(), obj); return file;