diff --git a/src/FeatureBuffer.cpp b/src/FeatureBuffer.cpp index d93d5da25..251dd88e7 100644 --- a/src/FeatureBuffer.cpp +++ b/src/FeatureBuffer.cpp @@ -8,6 +8,8 @@ #include "Features/TerrainOcclusion.h" #include "Features/WetnessEffects.h" +#include "TruePBR.h" + template std::pair _GetFeatureBufferData(Ts... feat_datas) { @@ -34,5 +36,5 @@ std::pair GetFeatureBufferData() WetnessEffects::GetSingleton()->GetCommonBufferData(), LightLimitFix::GetSingleton()->GetCommonBufferData(), Skylighting::GetSingleton()->cbData, - State::GetSingleton()->pbrSettings); + TruePBR::GetSingleton()->settings); } \ No newline at end of file diff --git a/src/Hooks.cpp b/src/Hooks.cpp index bb5ed7335..d41e7bbac 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -2,11 +2,10 @@ #include -#include "BSLightingShaderMaterialPBR.h" -#include "BSLightingShaderMaterialPBRLandscape.h" #include "Menu.h" #include "ShaderCache.h" #include "State.h" +#include "TruePBR.h" #include "Util.h" #include "ShaderTools/BSShaderHooks.h" @@ -84,156 +83,14 @@ void hk_BSShader_LoadShaders(RE::BSShader* shader, std::uintptr_t stream); decltype(&hk_BSShader_LoadShaders) ptr_BSShader_LoadShaders; -namespace Permutations -{ - template - std::unordered_set GenerateFlagPermutations(const RangeType& flags, uint32_t constantFlags) - { - std::vector flagValues; - std::ranges::transform(flags, std::back_inserter(flagValues), [](auto flag) { return static_cast(flag); }); - const uint32_t size = static_cast(flagValues.size()); - - std::unordered_set result; - for (uint32_t mask = 0; mask < (1u << size); ++mask) { - uint32_t flag = constantFlags; - for (size_t index = 0; index < size; ++index) { - if (mask & (1 << index)) { - flag |= flagValues[index]; - } - } - result.insert(flag); - } - - return result; - } - - uint32_t GetLightingShaderDescriptor(SIE::ShaderCache::LightingShaderTechniques technique, uint32_t flags) - { - return ((static_cast(technique) & 0x3F) << 24) | flags; - } - - void AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques technique, const std::unordered_set& flags, std::unordered_set& result) - { - for (uint32_t flag : flags) { - result.insert(GetLightingShaderDescriptor(technique, flag)); - } - } - - std::unordered_set GeneratePBRLightingVertexPermutations() - { - using enum SIE::ShaderCache::LightingShaderFlags; - - constexpr std::array defaultFlags{ VC, Skinned, WorldMap }; - constexpr std::array projectedUvFlags{ VC, WorldMap }; - constexpr std::array treeFlags{ VC, Skinned }; - constexpr std::array landFlags{ VC }; - - constexpr uint32_t defaultConstantFlags = static_cast(TruePbr); - constexpr uint32_t projectedUvConstantFlags = static_cast(TruePbr) | static_cast(ProjectedUV); - - const std::unordered_set defaultFlagValues = GenerateFlagPermutations(defaultFlags, defaultConstantFlags); - const std::unordered_set projectedUvFlagValues = GenerateFlagPermutations(projectedUvFlags, projectedUvConstantFlags); - const std::unordered_set treeFlagValues = GenerateFlagPermutations(treeFlags, defaultConstantFlags); - const std::unordered_set landFlagValues = GenerateFlagPermutations(landFlags, defaultConstantFlags); - - std::unordered_set result; - AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::None, defaultFlagValues, result); - AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::None, projectedUvFlagValues, result); - AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::TreeAnim, treeFlagValues, result); - AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLand, landFlagValues, result); - AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLandLODBlend, landFlagValues, result); - return result; - } - - std::unordered_set GeneratePBRLightingPixelPermutations() - { - using enum SIE::ShaderCache::LightingShaderFlags; - - constexpr std::array defaultFlags{ Skinned, DoAlphaTest, AdditionalAlphaMask }; - constexpr std::array projectedUvFlags{ DoAlphaTest, AdditionalAlphaMask, Snow, BaseObjectIsSnow }; - constexpr std::array lodObjectsFlags{ WorldMap, DoAlphaTest, AdditionalAlphaMask, ProjectedUV }; - constexpr std::array treeFlags{ Skinned, DoAlphaTest, AdditionalAlphaMask }; - - constexpr uint32_t defaultConstantFlags = static_cast(TruePbr) | static_cast(VC); - constexpr uint32_t projectedUvConstantFlags = static_cast(TruePbr) | static_cast(VC) | static_cast(ProjectedUV); - - const std::unordered_set defaultFlagValues = GenerateFlagPermutations(defaultFlags, defaultConstantFlags); - const std::unordered_set projectedUvFlagValues = GenerateFlagPermutations(projectedUvFlags, projectedUvConstantFlags); - const std::unordered_set lodObjectsFlagValues = GenerateFlagPermutations(lodObjectsFlags, defaultConstantFlags); - const std::unordered_set treeFlagValues = GenerateFlagPermutations(treeFlags, defaultConstantFlags); - const std::unordered_set landFlagValues = { defaultConstantFlags }; - - std::unordered_set result; - AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::None, defaultFlagValues, result); - AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::None, projectedUvFlagValues, result); - AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::LODObjects, lodObjectsFlagValues, result); - AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::LODObjectHD, lodObjectsFlagValues, result); - AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::TreeAnim, treeFlagValues, result); - AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLand, landFlagValues, result); - AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLandLODBlend, landFlagValues, result); - return result; - } - - std::unordered_set GeneratePBRGrassPermutations() - { - using enum SIE::ShaderCache::GrassShaderTechniques; - using enum SIE::ShaderCache::GrassShaderFlags; - - return { static_cast(TruePbr), - static_cast(TruePbr) | static_cast(AlphaTest) }; - } - - std::unordered_set GeneratePBRGrassVertexPermutations() - { - return GeneratePBRGrassPermutations(); - } - - std::unordered_set GeneratePBRGrassPixelPermutations() - { - return GeneratePBRGrassPermutations(); - } -} - void hk_BSShader_LoadShaders(RE::BSShader* shader, std::uintptr_t stream) { (ptr_BSShader_LoadShaders)(shader, stream); auto& shaderCache = SIE::ShaderCache::Instance(); if (shaderCache.IsDiskCache() || shaderCache.IsDump()) { - if (shaderCache.IsDiskCache() && shader->shaderType == RE::BSShader::Type::Lighting) { - const auto vertexPermutations = Permutations::GeneratePBRLightingVertexPermutations(); - for (auto descriptor : vertexPermutations) { - auto vertexShaderDesriptor = descriptor; - auto pixelShaderDescriptor = descriptor; - State::GetSingleton()->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor); - std::ignore = shaderCache.GetVertexShader(*shader, vertexShaderDesriptor); - } - - const auto pixelPermutations = Permutations::GeneratePBRLightingPixelPermutations(); - for (auto descriptor : pixelPermutations) { - auto vertexShaderDesriptor = descriptor; - auto pixelShaderDescriptor = descriptor; - State::GetSingleton()->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor); - std::ignore = shaderCache.GetPixelShader(*shader, pixelShaderDescriptor); - } - } - - if (shaderCache.IsDiskCache() && shader->shaderType == RE::BSShader::Type::Grass) { - const auto vertexPermutations = Permutations::GeneratePBRGrassVertexPermutations(); - for (auto descriptor : vertexPermutations) { - auto vertexShaderDesriptor = descriptor; - auto pixelShaderDescriptor = descriptor; - State::GetSingleton()->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor); - std::ignore = shaderCache.GetVertexShader(*shader, vertexShaderDesriptor); - } - - const auto pixelPermutations = Permutations::GeneratePBRGrassPixelPermutations(); - for (auto descriptor : pixelPermutations) { - auto vertexShaderDesriptor = descriptor; - auto pixelShaderDescriptor = descriptor; - State::GetSingleton()->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor); - std::ignore = shaderCache.GetPixelShader(*shader, pixelShaderDescriptor); - } + if (shaderCache.IsDiskCache()) { + TruePBR::GetSingleton()->GenerateShaderPermutations(shader); } for (const auto& entry : shader->vertexShaders) { @@ -262,11 +119,9 @@ void hk_BSShader_LoadShaders(RE::BSShader* shader, std::uintptr_t stream) BSShaderHooks::hk_LoadShaders((REX::BSShader*)shader, stream); }; -bool hk_BSShader_BeginTechnique(RE::BSShader* shader, uint32_t vertexDescriptor, uint32_t pixelDescriptor, bool skipPixelShader); - -decltype(&hk_BSShader_BeginTechnique) ptr_BSShader_BeginTechnique; +decltype(&Hooks::hk_BSShader_BeginTechnique) ptr_BSShader_BeginTechnique; -bool hk_BSShader_BeginTechnique(RE::BSShader* shader, uint32_t vertexDescriptor, uint32_t pixelDescriptor, bool skipPixelShader) +bool Hooks::hk_BSShader_BeginTechnique(RE::BSShader* shader, uint32_t vertexDescriptor, uint32_t pixelDescriptor, bool skipPixelShader) { auto state = State::GetSingleton(); @@ -316,30 +171,6 @@ HRESULT WINAPI hk_IDXGISwapChain_Present(IDXGISwapChain* This, UINT SyncInterval return (This->*ptr_IDXGISwapChain_Present)(SyncInterval, Flags); } -struct ExtendedRendererState -{ - static constexpr uint32_t NumPSTextures = 12; - static constexpr uint32_t FirstPSTexture = 80; - - uint32_t PSResourceModifiedBits = 0; - ID3D11ShaderResourceView* PSTexture[NumPSTextures]; - - void SetPSTexture(size_t textureIndex, RE::BSGraphics::Texture* newTexture) - { - ID3D11ShaderResourceView* resourceView = newTexture ? newTexture->resourceView : nullptr; - //if (PSTexture[textureIndex] != resourceView) - { - PSTexture[textureIndex] = resourceView; - PSResourceModifiedBits |= (1 << textureIndex); - } - } - - ExtendedRendererState() - { - std::fill_n(PSTexture, NumPSTextures, nullptr); - } -} extendedRendererState; - void hk_BSGraphics_SetDirtyStates(bool isCompute); decltype(&hk_BSGraphics_SetDirtyStates) ptr_BSGraphics_SetDirtyStates; @@ -347,17 +178,6 @@ decltype(&hk_BSGraphics_SetDirtyStates) ptr_BSGraphics_SetDirtyStates; void hk_BSGraphics_SetDirtyStates(bool isCompute) { (ptr_BSGraphics_SetDirtyStates)(isCompute); - - { - auto context = State::GetSingleton()->context; - for (uint32_t textureIndex = 0; textureIndex < ExtendedRendererState::NumPSTextures; ++textureIndex) { - if (extendedRendererState.PSResourceModifiedBits & (1 << textureIndex)) { - context->PSSetShaderResources(ExtendedRendererState::FirstPSTexture + textureIndex, 1, &extendedRendererState.PSTexture[textureIndex]); - } - } - extendedRendererState.PSResourceModifiedBits = 0; - } - State::GetSingleton()->Draw(); } @@ -544,600 +364,6 @@ namespace Hooks static inline REL::Relocation func; }; - struct BSLightingShaderProperty_LoadBinary - { - static void thunk(RE::BSLightingShaderProperty* property, RE::NiStream& stream) - { - using enum RE::BSShaderProperty::EShaderPropertyFlag; - - RE::BSShaderMaterial::Feature feature = RE::BSShaderMaterial::Feature::kDefault; - stream.iStr->read(&feature, 1); - - { - auto vtable = REL::Relocation(RE::NiShadeProperty::VTABLE[0]); - auto baseMethod = reinterpret_cast((vtable.get()[0x18])); - baseMethod(property, stream); - } - - stream.iStr->read(&property->flags, 1); - - bool isPbr = false; - { - RE::BSLightingShaderMaterialBase* material = nullptr; - if (property->flags.any(kMenuScreen)) { - auto* pbrMaterial = BSLightingShaderMaterialPBR::Make(); - pbrMaterial->loadedWithFeature = feature; - material = pbrMaterial; - isPbr = true; - } else { - material = RE::BSLightingShaderMaterialBase::CreateMaterial(feature); - } - property->LinkMaterial(nullptr, false); - property->material = material; - } - - { - stream.iStr->read(&property->material->texCoordOffset[0].x, 1); - stream.iStr->read(&property->material->texCoordOffset[0].y, 1); - stream.iStr->read(&property->material->texCoordScale[0].x, 1); - stream.iStr->read(&property->material->texCoordScale[0].y, 1); - - property->material->texCoordOffset[1] = property->material->texCoordOffset[0]; - property->material->texCoordScale[1] = property->material->texCoordScale[0]; - } - - stream.LoadLinkID(); - - { - RE::NiColor emissiveColor{}; - stream.iStr->read(&emissiveColor.red, 1); - stream.iStr->read(&emissiveColor.green, 1); - stream.iStr->read(&emissiveColor.blue, 1); - - if (property->emissiveColor != nullptr && property->flags.any(kOwnEmit)) { - *property->emissiveColor = emissiveColor; - } - } - - stream.iStr->read(&property->emissiveMult, 1); - - static_cast(property->material)->LoadBinary(stream); - - if (isPbr) { - auto pbrMaterial = static_cast(property->material); - if (property->flags.any(kMultiLayerParallax)) { - pbrMaterial->pbrFlags.set(PBRFlags::TwoLayer); - if (property->flags.any(kSoftLighting)) { - pbrMaterial->pbrFlags.set(PBRFlags::InterlayerParallax); - } - if (property->flags.any(kBackLighting)) { - pbrMaterial->pbrFlags.set(PBRFlags::CoatNormal); - } - if (property->flags.any(kEffectLighting)) { - pbrMaterial->pbrFlags.set(PBRFlags::ColoredCoat); - } - } else if (property->flags.any(kBackLighting)) { - pbrMaterial->pbrFlags.set(PBRFlags::HairMarschner); - } else { - if (property->flags.any(kRimLighting)) { - pbrMaterial->pbrFlags.set(PBRFlags::Subsurface); - } - if (property->flags.any(kSoftLighting)) { - pbrMaterial->pbrFlags.set(PBRFlags::Fuzz); - } - } - property->flags.set(kVertexLighting); - property->flags.reset(kMenuScreen, kSpecular, kGlowMap, kEnvMap, kMultiLayerParallax, kSoftLighting, kRimLighting, kBackLighting, kAnisotropicLighting, kEffectLighting); - } - } - static inline REL::Relocation func; - }; - - struct BSLightingShaderProperty_GetRenderPasses - { - static RE::BSShaderProperty::RenderPassArray* thunk(RE::BSLightingShaderProperty* property, RE::BSGeometry* geometry, std::uint32_t renderFlags, RE::BSShaderAccumulator* accumulator) - { - auto renderPasses = func(property, geometry, renderFlags, accumulator); - if (renderPasses == nullptr) { - return renderPasses; - } - - bool isPbr = false; - - if (property->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kVertexLighting) && (property->material->GetFeature() == RE::BSShaderMaterial::Feature::kDefault || property->material->GetFeature() == RE::BSShaderMaterial::Feature::kMultiTexLandLODBlend)) { - isPbr = true; - } - - auto currentPass = renderPasses->head; - while (currentPass != nullptr) { - if (currentPass->shader->shaderType == RE::BSShader::Type::Lighting) { - constexpr uint32_t LightingTechniqueStart = 0x4800002D; - auto lightingTechnique = currentPass->passEnum - LightingTechniqueStart; - auto lightingFlags = lightingTechnique & ~(~0u << 24); - auto lightingType = static_cast((lightingTechnique >> 24) & 0x3F); - lightingFlags &= ~0b111000u; - if (isPbr) { - lightingFlags |= static_cast(SIE::ShaderCache::LightingShaderFlags::TruePbr); - } - lightingTechnique = (static_cast(lightingType) << 24) | lightingFlags; - currentPass->passEnum = lightingTechnique + LightingTechniqueStart; - } - currentPass = currentPass->next; - } - - return renderPasses; - } - static inline REL::Relocation func; - }; - - struct BSLightingShader_SetupMaterial - { - static void thunk(RE::BSLightingShader* shader, RE::BSLightingShaderMaterialBase const* material) - { - using enum SIE::ShaderCache::LightingShaderTechniques; - - auto lightingFlags = shader->currentRawTechnique & ~(~0u << 24); - auto lightingType = static_cast((shader->currentRawTechnique >> 24) & 0x3F); - if (!(lightingType == LODLand || lightingType == LODLandNoise) && (lightingFlags & static_cast(SIE::ShaderCache::LightingShaderFlags::TruePbr))) { - auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); - auto renderer = RE::BSGraphics::Renderer::GetSingleton(); - - RE::BSGraphics::Renderer::PrepareVSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); - RE::BSGraphics::Renderer::PreparePSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); - - if (lightingType == MTLand || lightingType == MTLandLODBlend) { - auto* pbrMaterial = static_cast(material); - - constexpr size_t NormalStartIndex = 7; - - if (pbrMaterial->diffuseTexture != nullptr) { - shadowState->SetPSTexture(0, pbrMaterial->diffuseTexture->rendererTexture); - } - if (pbrMaterial->normalTexture != nullptr) { - shadowState->SetPSTexture(NormalStartIndex, pbrMaterial->normalTexture->rendererTexture); - } - if (pbrMaterial->landscapeDisplacementTextures[0] != nullptr) { - extendedRendererState.SetPSTexture(0, pbrMaterial->landscapeDisplacementTextures[0]->rendererTexture); - } - if (pbrMaterial->landscapeRMAOSTextures[0] != nullptr) { - extendedRendererState.SetPSTexture(BSLightingShaderMaterialPBRLandscape::NumTiles, pbrMaterial->landscapeRMAOSTextures[0]->rendererTexture); - } - for (uint32_t textureIndex = 1; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles; ++textureIndex) { - if (pbrMaterial->landscapeBaseColorTextures[textureIndex - 1] != nullptr) { - shadowState->SetPSTexture(textureIndex, pbrMaterial->landscapeBaseColorTextures[textureIndex - 1]->rendererTexture); - } - if (pbrMaterial->landscapeNormalTextures[textureIndex - 1] != nullptr) { - shadowState->SetPSTexture(NormalStartIndex + textureIndex, pbrMaterial->landscapeNormalTextures[textureIndex - 1]->rendererTexture); - } - if (pbrMaterial->landscapeDisplacementTextures[textureIndex] != nullptr) { - extendedRendererState.SetPSTexture(textureIndex, pbrMaterial->landscapeDisplacementTextures[textureIndex]->rendererTexture); - } - if (pbrMaterial->landscapeRMAOSTextures[textureIndex] != nullptr) { - extendedRendererState.SetPSTexture(BSLightingShaderMaterialPBRLandscape::NumTiles + textureIndex, pbrMaterial->landscapeRMAOSTextures[textureIndex]->rendererTexture); - } - } - - shadowState->SetPSTextureAddressMode(0, RE::BSGraphics::TextureAddressMode::kWrapSWrapT); - shadowState->SetPSTextureFilterMode(0, RE::BSGraphics::TextureFilterMode::kAnisotropic); - - if (pbrMaterial->terrainOverlayTexture != nullptr) { - shadowState->SetPSTexture(13, pbrMaterial->terrainOverlayTexture->rendererTexture); - shadowState->SetPSTextureAddressMode(13, RE::BSGraphics::TextureAddressMode::kClampSClampT); - shadowState->SetPSTextureFilterMode(13, RE::BSGraphics::TextureFilterMode::kAnisotropic); - } - - if (pbrMaterial->terrainNoiseTexture != nullptr) { - shadowState->SetPSTexture(15, pbrMaterial->terrainNoiseTexture->rendererTexture); - shadowState->SetPSTextureAddressMode(15, RE::BSGraphics::TextureAddressMode::kWrapSWrapT); - shadowState->SetPSTextureFilterMode(15, RE::BSGraphics::TextureFilterMode::kBilinear); - } - - { - uint32_t flags = 0; - for (uint32_t textureIndex = 0; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles; ++textureIndex) { - if (pbrMaterial->isPbr[textureIndex]) { - flags |= (1 << textureIndex); - if (pbrMaterial->landscapeDisplacementTextures[textureIndex] != nullptr && pbrMaterial->landscapeDisplacementTextures[textureIndex] != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack) { - flags |= (1 << (BSLightingShaderMaterialPBRLandscape::NumTiles + textureIndex)); - } - } - } - shadowState->SetPSConstant(flags, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 36); - } - - { - constexpr size_t PBRParamsStartIndex = 37; - - for (uint32_t textureIndex = 0; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles; ++textureIndex) { - std::array PBRParams; - PBRParams[0] = pbrMaterial->roughnessScales[textureIndex]; - PBRParams[1] = pbrMaterial->displacementScales[textureIndex]; - PBRParams[2] = pbrMaterial->specularLevels[textureIndex]; - shadowState->SetPSConstant(PBRParams, RE::BSGraphics::ConstantGroupLevel::PerMaterial, PBRParamsStartIndex + textureIndex); - } - } - - { - std::array lodTexParams; - lodTexParams[0] = pbrMaterial->terrainTexOffsetX; - lodTexParams[1] = pbrMaterial->terrainTexOffsetY; - lodTexParams[2] = 1.f; - lodTexParams[3] = pbrMaterial->terrainTexFade; - shadowState->SetPSConstant(lodTexParams, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 24); - } - } else if (lightingType == None || lightingType == TreeAnim) { - auto* pbrMaterial = static_cast(material); - if (pbrMaterial->diffuseRenderTargetSourceIndex != -1) { - shadowState->SetPSTexture(0, renderer->GetRuntimeData().renderTargets[pbrMaterial->diffuseRenderTargetSourceIndex]); - } else { - shadowState->SetPSTexture(0, pbrMaterial->diffuseTexture->rendererTexture); - } - shadowState->SetPSTextureAddressMode(0, static_cast(pbrMaterial->textureClampMode)); - shadowState->SetPSTextureFilterMode(0, RE::BSGraphics::TextureFilterMode::kAnisotropic); - - shadowState->SetPSTexture(1, pbrMaterial->normalTexture->rendererTexture); - shadowState->SetPSTextureAddressMode(1, static_cast(pbrMaterial->textureClampMode)); - shadowState->SetPSTextureFilterMode(1, RE::BSGraphics::TextureFilterMode::kAnisotropic); - - shadowState->SetPSTexture(5, pbrMaterial->rmaosTexture->rendererTexture); - shadowState->SetPSTextureAddressMode(5, static_cast(pbrMaterial->textureClampMode)); - shadowState->SetPSTextureFilterMode(5, RE::BSGraphics::TextureFilterMode::kAnisotropic); - - stl::enumeration shaderFlags; - if (pbrMaterial->pbrFlags.any(PBRFlags::TwoLayer)) { - shaderFlags.set(PBRShaderFlags::TwoLayer); - if (pbrMaterial->pbrFlags.any(PBRFlags::InterlayerParallax)) { - shaderFlags.set(PBRShaderFlags::InterlayerParallax); - } - if (pbrMaterial->pbrFlags.any(PBRFlags::CoatNormal)) { - shaderFlags.set(PBRShaderFlags::CoatNormal); - } - if (pbrMaterial->pbrFlags.any(PBRFlags::ColoredCoat)) { - shaderFlags.set(PBRShaderFlags::ColoredCoat); - } - - std::array PBRParams2; - PBRParams2[0] = pbrMaterial->GetCoatColor().red; - PBRParams2[1] = pbrMaterial->GetCoatColor().green; - PBRParams2[2] = pbrMaterial->GetCoatColor().blue; - PBRParams2[3] = pbrMaterial->GetCoatStrength(); - shadowState->SetPSConstant(PBRParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 43); - - std::array PBRParams3; - PBRParams3[0] = pbrMaterial->GetCoatRoughness(); - PBRParams3[1] = pbrMaterial->GetCoatSpecularLevel(); - shadowState->SetPSConstant(PBRParams3, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 27); - } else if (pbrMaterial->pbrFlags.any(PBRFlags::HairMarschner)) { - shaderFlags.set(PBRShaderFlags::HairMarschner); - } else { - if (pbrMaterial->pbrFlags.any(PBRFlags::Subsurface)) { - shaderFlags.set(PBRShaderFlags::Subsurface); - - std::array PBRParams2; - PBRParams2[0] = pbrMaterial->GetSubsurfaceColor().red; - PBRParams2[1] = pbrMaterial->GetSubsurfaceColor().green; - PBRParams2[2] = pbrMaterial->GetSubsurfaceColor().blue; - PBRParams2[3] = pbrMaterial->GetSubsurfaceOpacity(); - shadowState->SetPSConstant(PBRParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 43); - } - if (pbrMaterial->pbrFlags.any(PBRFlags::Fuzz)) { - shaderFlags.set(PBRShaderFlags::Fuzz); - - std::array PBRParams3; - PBRParams3[0] = pbrMaterial->GetFuzzColor().red; - PBRParams3[1] = pbrMaterial->GetFuzzColor().green; - PBRParams3[2] = pbrMaterial->GetFuzzColor().blue; - PBRParams3[3] = pbrMaterial->GetFuzzWeight(); - shadowState->SetPSConstant(PBRParams3, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 27); - } - } - - { - std::array PBRProjectedUVParams1; - PBRProjectedUVParams1[0] = pbrMaterial->GetProjectedMaterialBaseColorScale()[0]; - PBRProjectedUVParams1[1] = pbrMaterial->GetProjectedMaterialBaseColorScale()[1]; - PBRProjectedUVParams1[2] = pbrMaterial->GetProjectedMaterialBaseColorScale()[2]; - shadowState->SetPSConstant(PBRProjectedUVParams1, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 21); - - std::array PBRProjectedUVParams2; - PBRProjectedUVParams2[0] = pbrMaterial->GetProjectedMaterialRoughness(); - PBRProjectedUVParams2[1] = pbrMaterial->GetProjectedMaterialSpecularLevel(); - shadowState->SetPSConstant(PBRProjectedUVParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 22); - } - - const bool hasEmissive = pbrMaterial->emissiveTexture != nullptr && pbrMaterial->emissiveTexture != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack; - if (hasEmissive) { - shadowState->SetPSTexture(6, pbrMaterial->emissiveTexture->rendererTexture); - shadowState->SetPSTextureAddressMode(6, static_cast(pbrMaterial->textureClampMode)); - shadowState->SetPSTextureFilterMode(6, RE::BSGraphics::TextureFilterMode::kAnisotropic); - - shaderFlags.set(PBRShaderFlags::HasEmissive); - } - - const bool hasDisplacement = pbrMaterial->displacementTexture != nullptr && pbrMaterial->displacementTexture != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack; - if (hasDisplacement) { - shadowState->SetPSTexture(4, pbrMaterial->displacementTexture->rendererTexture); - shadowState->SetPSTextureAddressMode(4, static_cast(pbrMaterial->textureClampMode)); - shadowState->SetPSTextureFilterMode(4, RE::BSGraphics::TextureFilterMode::kAnisotropic); - - shaderFlags.set(PBRShaderFlags::HasDisplacement); - } - - const bool hasFeaturesTexture0 = pbrMaterial->featuresTexture0 != nullptr && pbrMaterial->featuresTexture0 != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureWhite; - if (hasFeaturesTexture0) { - shadowState->SetPSTexture(12, pbrMaterial->featuresTexture0->rendererTexture); - shadowState->SetPSTextureAddressMode(12, static_cast(pbrMaterial->textureClampMode)); - shadowState->SetPSTextureFilterMode(12, RE::BSGraphics::TextureFilterMode::kAnisotropic); - - shaderFlags.set(PBRShaderFlags::HasFeaturesTexture0); - } - - const bool hasFeaturesTexture1 = pbrMaterial->featuresTexture1 != nullptr && pbrMaterial->featuresTexture1 != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureWhite; - if (hasFeaturesTexture1) { - shadowState->SetPSTexture(9, pbrMaterial->featuresTexture1->rendererTexture); - shadowState->SetPSTextureAddressMode(9, static_cast(pbrMaterial->textureClampMode)); - shadowState->SetPSTextureFilterMode(9, RE::BSGraphics::TextureFilterMode::kAnisotropic); - - shaderFlags.set(PBRShaderFlags::HasFeaturesTexture1); - } - - { - shadowState->SetPSConstant(shaderFlags, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 36); - } - - { - std::array PBRParams1; - PBRParams1[0] = pbrMaterial->GetRoughnessScale(); - PBRParams1[1] = pbrMaterial->GetDisplacementScale(); - PBRParams1[2] = pbrMaterial->GetSpecularLevel(); - shadowState->SetPSConstant(PBRParams1, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 37); - } - } - - { - const uint32_t bufferIndex = RE::BSShaderManager::State::GetSingleton().textureTransformCurrentBuffer; - - std::array texCoordOffsetScale; - texCoordOffsetScale[0] = material->texCoordOffset[bufferIndex].x; - texCoordOffsetScale[1] = material->texCoordOffset[bufferIndex].y; - texCoordOffsetScale[2] = material->texCoordScale[bufferIndex].x; - texCoordOffsetScale[3] = material->texCoordScale[bufferIndex].y; - shadowState->SetVSConstant(texCoordOffsetScale, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 11); - } - - if (lightingFlags & static_cast(SIE::ShaderCache::LightingShaderFlags::CharacterLight)) { - static const REL::Relocation characterLightTexture{ RELOCATION_ID(513464, 391302) }; - - if (characterLightTexture->renderTarget >= RE::RENDER_TARGET::kFRAMEBUFFER) { - shadowState->SetPSTexture(11, renderer->GetRuntimeData().renderTargets[characterLightTexture->renderTarget]); - shadowState->SetPSTextureAddressMode(11, RE::BSGraphics::TextureAddressMode::kClampSClampT); - } - - const auto& smState = RE::BSShaderManager::State::GetSingleton(); - std::array characterLightParams; - if (smState.characterLightEnabled) { - std::copy_n(smState.characterLightParams, 4, characterLightParams.data()); - } else { - std::fill_n(characterLightParams.data(), 4, 0.f); - } - shadowState->SetPSConstant(characterLightParams, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 35); - } - - RE::BSGraphics::Renderer::FlushVSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); - RE::BSGraphics::Renderer::FlushPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); - RE::BSGraphics::Renderer::ApplyVSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); - RE::BSGraphics::Renderer::ApplyPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); - } else { - func(shader, material); - } - }; - static inline REL::Relocation func; - }; - - struct BSLightingShader_SetupGeometry - { - static void thunk(RE::BSLightingShader* shader, RE::BSRenderPass* pass, uint32_t renderFlags) - { - const uint32_t originalExtraFlags = shader->currentRawTechnique & 0b111000u; - - if ((shader->currentRawTechnique & static_cast(SIE::ShaderCache::LightingShaderFlags::TruePbr)) != 0) { - shader->currentRawTechnique |= static_cast(SIE::ShaderCache::LightingShaderFlags::AmbientSpecular); - } - - shader->currentRawTechnique &= ~0b111000u; - shader->currentRawTechnique |= ((pass->numLights - 1) << 3); - - func(shader, pass, renderFlags); - - shader->currentRawTechnique &= ~0b111000u; - shader->currentRawTechnique |= originalExtraFlags; - - if ((shader->currentRawTechnique & static_cast(SIE::ShaderCache::LightingShaderFlags::TruePbr)) != 0) { - shader->currentRawTechnique &= ~static_cast(SIE::ShaderCache::LightingShaderFlags::AmbientSpecular); - } - } - static inline REL::Relocation func; - }; - - uint32_t hk_BSLightingShader_GetPixelTechnique(uint32_t rawTechnique) - { - uint32_t pixelTechnique = rawTechnique; - - pixelTechnique &= ~0b111000000u; - if ((pixelTechnique & static_cast(SIE::ShaderCache::LightingShaderFlags::ModelSpaceNormals)) == 0) { - pixelTechnique &= ~static_cast(SIE::ShaderCache::LightingShaderFlags::Skinned); - } - pixelTechnique |= static_cast(SIE::ShaderCache::LightingShaderFlags::VC); - - return pixelTechnique; - } - - void SetupLandscapeTexture(BSLightingShaderMaterialPBRLandscape& material, RE::TESLandTexture& landTexture, uint32_t textureIndex) - { - if (textureIndex >= 6) { - return; - } - - auto textureSet = landTexture.textureSet; - if (textureSet == nullptr) { - return; - } - - auto* textureSetData = State::GetSingleton()->GetPBRTextureSetData(landTexture.textureSet); - const bool isPbr = textureSetData != nullptr; - - textureSet->SetTexture(BSLightingShaderMaterialPBRLandscape::BaseColorTexture, textureIndex == 0 ? material.diffuseTexture : material.landscapeBaseColorTextures[textureIndex - 1]); - textureSet->SetTexture(BSLightingShaderMaterialPBRLandscape::NormalTexture, textureIndex == 0 ? material.normalTexture : material.landscapeNormalTextures[textureIndex - 1]); - - if (isPbr) { - textureSet->SetTexture(BSLightingShaderMaterialPBRLandscape::RmaosTexture, material.landscapeRMAOSTextures[textureIndex]); - textureSet->SetTexture(BSLightingShaderMaterialPBRLandscape::DisplacementTexture, material.landscapeDisplacementTextures[textureIndex]); - material.displacementScales[textureIndex] = textureSetData->displacementScale; - material.roughnessScales[textureIndex] = textureSetData->roughnessScale; - material.specularLevels[textureIndex] = textureSetData->specularLevel; - } - material.isPbr[textureIndex] = isPbr; - - if (textureIndex == 0) { - if (material.diffuseTexture != nullptr) { - material.numLandscapeTextures = std::max(material.numLandscapeTextures, 1u); - } - } else { - if (material.landscapeBaseColorTextures[textureIndex] != nullptr) { - material.numLandscapeTextures = std::max(material.numLandscapeTextures, textureIndex + 2); - } - } - } - - bool hk_TESObjectLAND_SetupMaterial(RE::TESObjectLAND* land); - decltype(&hk_TESObjectLAND_SetupMaterial) ptr_TESObjectLAND_SetupMaterial; - - bool hk_TESObjectLAND_SetupMaterial(RE::TESObjectLAND* land) - { - auto* state = State::GetSingleton(); - - bool isPbr = false; - if (land->loadedData != nullptr) { - for (uint32_t quadIndex = 0; quadIndex < 4; ++quadIndex) { - if (land->loadedData->defQuadTextures[quadIndex] != nullptr) { - if (state->IsPBRTextureSet(land->loadedData->defQuadTextures[quadIndex]->textureSet)) { - isPbr = true; - break; - } - } - for (uint32_t textureIndex = 0; textureIndex < 6; ++textureIndex) { - if (land->loadedData->quadTextures[quadIndex][textureIndex] != nullptr) { - if (state->IsPBRTextureSet(land->loadedData->quadTextures[quadIndex][textureIndex]->textureSet)) { - isPbr = true; - break; - } - } - } - } - } - - if (!isPbr) { - return ptr_TESObjectLAND_SetupMaterial(land); - } - - static const auto settings = RE::INISettingCollection::GetSingleton(); - static const bool bEnableLandFade = settings->GetSetting("bEnableLandFade:Display"); - static const bool bDrawLandShadows = settings->GetSetting("bDrawLandShadows:Display"); - static const bool bLandSpecular = settings->GetSetting("bLandSpecular:Landscape"); - - if (land->loadedData != nullptr && land->loadedData->mesh[0] != nullptr) { - land->data.flags.set(static_cast(8)); - for (uint32_t quadIndex = 0; quadIndex < 4; ++quadIndex) { - auto shaderProperty = static_cast(RE::MemoryManager::GetSingleton()->Allocate(sizeof(RE::BSLightingShaderProperty), 0, false)); - shaderProperty->Ctor(); - - { - BSLightingShaderMaterialPBRLandscape srcMaterial; - shaderProperty->LinkMaterial(&srcMaterial, true); - } - - auto material = static_cast(shaderProperty->material); - const auto& stateData = RE::BSGraphics::State::GetSingleton()->GetRuntimeData(); - - material->diffuseTexture = stateData.defaultTextureBlack; - material->normalTexture = stateData.defaultTextureNormalMap; - material->landscapeDisplacementTextures[0] = stateData.defaultTextureBlack; - material->landscapeRMAOSTextures[0] = stateData.defaultTextureWhite; - for (uint32_t textureIndex = 0; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles - 1; ++textureIndex) { - material->landscapeBaseColorTextures[textureIndex] = stateData.defaultTextureBlack; - material->landscapeNormalTextures[textureIndex] = stateData.defaultTextureNormalMap; - material->landscapeDisplacementTextures[textureIndex + 1] = stateData.defaultTextureBlack; - material->landscapeRMAOSTextures[textureIndex + 1] = stateData.defaultTextureWhite; - } - - if (auto defTexture = land->loadedData->defQuadTextures[quadIndex]) { - SetupLandscapeTexture(*material, *defTexture, 0); - } - for (uint32_t textureIndex = 0; textureIndex < 6; ++textureIndex) { - if (auto landTexture = land->loadedData->quadTextures[quadIndex][textureIndex]) { - SetupLandscapeTexture(*material, *landTexture, textureIndex + 1); - } - } - - if (bEnableLandFade) { - shaderProperty->unk108 = false; - } - - bool noLODLandBlend = false; - auto tes = RE::TES::GetSingleton(); - if (tes->worldSpace != nullptr) { - if (auto terrainManager = tes->worldSpace->GetTerrainManager()) { - noLODLandBlend = reinterpret_cast(terrainManager)[0x36]; - } - } - shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kMultiTextureLandscape, true); - shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kReceiveShadows, true); - shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kCastShadows, bDrawLandShadows); - shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kNoLODLandBlend, noLODLandBlend); - - shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kVertexLighting, true); - - const auto& children = land->loadedData->mesh[quadIndex]->GetChildren(); - auto geometry = children.empty() ? nullptr : static_cast(children[0].get()); - shaderProperty->SetupGeometry(geometry); - if (geometry != nullptr) { - geometry->GetGeometryRuntimeData().properties[1] = RE::NiPointer(shaderProperty); - } - - RE::BSShaderManager::State::GetSingleton().shadowSceneNode[0]->AttachObject(geometry); - } - - return true; - } - - return false; - } - - struct TESForm_GetFormEditorID - { - static const char* thunk(const RE::TESForm* form) - { - auto* state = State::GetSingleton(); - auto it = state->editorIDs.find(form->GetFormID()); - if (it == state->editorIDs.cend()) { - return ""; - } - return it->second.c_str(); - } - static inline REL::Relocation func; - }; - - struct TESForm_SetFormEditorID - { - static bool thunk(RE::TESForm* form, const char* editorId) - { - auto* state = State::GetSingleton(); - state->editorIDs[form->GetFormID()] = editorId; - return true; - } - static inline REL::Relocation func; - }; - struct CreateRenderTarget_Normals { static void thunk(RE::BSGraphics::Renderer* This, RE::RENDER_TARGETS::RENDER_TARGET a_target, RE::BSGraphics::RenderTargetProperties* a_properties) @@ -1255,299 +481,6 @@ namespace Hooks static inline REL::Relocation func; }; - void hk_SetPerFrameBuffers(void* renderer); - decltype(&hk_SetPerFrameBuffers) ptr_SetPerFrameBuffers; - - void hk_SetPerFrameBuffers(void* renderer) - { - ptr_SetPerFrameBuffers(renderer); - State::GetSingleton()->SetupFrame(); - } - - void hk_BSTempEffectSimpleDecal_SetupGeometry(RE::BSTempEffectSimpleDecal* decal, RE::BSGeometry* geometry, RE::BGSTextureSet* textureSet, bool blended); - decltype(&hk_BSTempEffectSimpleDecal_SetupGeometry) ptr_BSTempEffectSimpleDecal_SetupGeometry; - - void hk_BSTempEffectSimpleDecal_SetupGeometry(RE::BSTempEffectSimpleDecal* decal, RE::BSGeometry* geometry, RE::BGSTextureSet* textureSet, bool blended) - { - ptr_BSTempEffectSimpleDecal_SetupGeometry(decal, geometry, textureSet, blended); - - if (auto* shaderProperty = netimmerse_cast(geometry->GetGeometryRuntimeData().properties[1].get()); - shaderProperty != nullptr && State::GetSingleton()->IsPBRTextureSet(textureSet)) { - { - BSLightingShaderMaterialPBR srcMaterial; - shaderProperty->LinkMaterial(&srcMaterial, true); - } - - auto pbrMaterial = static_cast(shaderProperty->material); - pbrMaterial->OnLoadTextureSet(0, textureSet); - - constexpr static RE::NiColor whiteColor(1.f, 1.f, 1.f); - *shaderProperty->emissiveColor = whiteColor; - const bool hasEmissive = pbrMaterial->emissiveTexture != nullptr && pbrMaterial->emissiveTexture != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack; - shaderProperty->emissiveMult = hasEmissive ? 1.f : 0.f; - - { - using enum RE::BSShaderProperty::EShaderPropertyFlag8; - shaderProperty->SetFlags(kParallaxOcclusion, false); - shaderProperty->SetFlags(kParallax, false); - shaderProperty->SetFlags(kGlowMap, false); - shaderProperty->SetFlags(kEnvMap, false); - shaderProperty->SetFlags(kSpecular, false); - - shaderProperty->SetFlags(kVertexLighting, true); - } - } - } - - struct BSTempEffectGeometryDecal_Initialize - { - static void thunk(RE::BSTempEffectGeometryDecal* decal) - { - func(decal); - - if (decal->decal != nullptr && State::GetSingleton()->IsPBRTextureSet(decal->texSet)) { - auto shaderProperty = static_cast(RE::MemoryManager::GetSingleton()->Allocate(sizeof(RE::BSLightingShaderProperty), 0, false)); - shaderProperty->Ctor(); - - { - BSLightingShaderMaterialPBR srcMaterial; - shaderProperty->LinkMaterial(&srcMaterial, true); - } - - auto pbrMaterial = static_cast(shaderProperty->material); - pbrMaterial->OnLoadTextureSet(0, decal->texSet); - - constexpr static RE::NiColor whiteColor(1.f, 1.f, 1.f); - *shaderProperty->emissiveColor = whiteColor; - const bool hasEmissive = pbrMaterial->emissiveTexture != nullptr && pbrMaterial->emissiveTexture != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack; - shaderProperty->emissiveMult = hasEmissive ? 1.f : 0.f; - - { - using enum RE::BSShaderProperty::EShaderPropertyFlag8; - - shaderProperty->SetFlags(kSkinned, true); - shaderProperty->SetFlags(kDynamicDecal, true); - shaderProperty->SetFlags(kZBufferTest, true); - shaderProperty->SetFlags(kZBufferWrite, false); - - shaderProperty->SetFlags(kVertexLighting, true); - } - - if (auto* alphaProperty = static_cast(decal->decal->GetGeometryRuntimeData().properties[0].get())) { - alphaProperty->alphaFlags = (alphaProperty->alphaFlags & ~0x1FE) | 0xED; - } - - shaderProperty->SetupGeometry(decal->decal.get()); - decal->decal->GetGeometryRuntimeData().properties[1] = RE::NiPointer(shaderProperty); - } - } - static inline REL::Relocation func; - }; - - struct BSGrassShaderProperty_ctor - { - static RE::BSLightingShaderProperty* thunk(RE::BSLightingShaderProperty* property) - { - const uint64_t stackPointer = reinterpret_cast(_AddressOfReturnAddress()); - const uint64_t lightingPropertyAddress = stackPointer + (REL::Module::IsAE() ? 0x68 : 0x70); - auto* lightingProperty = *reinterpret_cast(lightingPropertyAddress); - - RE::BSLightingShaderProperty* grassProperty = func(property); - - if (lightingProperty->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kVertexLighting)) { - if (auto* pbrSrcMaterial = static_cast(lightingProperty->material)) { - BSLightingShaderMaterialPBR srcMaterial; - grassProperty->LinkMaterial(&srcMaterial, true); - - grassProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kMenuScreen, true); - - auto pbrMaterial = static_cast(grassProperty->material); - pbrMaterial->pbrFlags = pbrSrcMaterial->pbrFlags; - pbrMaterial->normalTexture = pbrSrcMaterial->normalTexture; - pbrMaterial->rmaosTexture = pbrSrcMaterial->rmaosTexture; - pbrMaterial->featuresTexture0 = pbrSrcMaterial->featuresTexture0; - pbrMaterial->featuresTexture1 = pbrSrcMaterial->featuresTexture1; - pbrMaterial->specularColorScale = pbrSrcMaterial->specularColorScale; - pbrMaterial->specularPower = pbrSrcMaterial->specularPower; - pbrMaterial->specularColor = pbrSrcMaterial->specularColor; - pbrMaterial->subSurfaceLightRolloff = pbrSrcMaterial->subSurfaceLightRolloff; - pbrMaterial->coatRoughness = pbrSrcMaterial->coatRoughness; - } - } - - return grassProperty; - } - static inline REL::Relocation func; - }; - - struct BSGrassShaderProperty_GetRenderPasses - { - static RE::BSShaderProperty::RenderPassArray* thunk(RE::BSLightingShaderProperty* property, RE::BSGeometry* geometry, std::uint32_t renderFlags, RE::BSShaderAccumulator* accumulator) - { - auto renderPasses = func(property, geometry, renderFlags, accumulator); - if (renderPasses == nullptr) { - return renderPasses; - } - - const bool isPbr = property->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kMenuScreen); - if (isPbr) { - auto currentPass = renderPasses->head; - while (currentPass != nullptr) { - if (currentPass->shader->shaderType == RE::BSShader::Type::Grass && currentPass->passEnum != 0x5C00005C) { - currentPass->passEnum = 0x5C000042; - } - currentPass = currentPass->next; - } - } - - return renderPasses; - } - static inline REL::Relocation func; - }; - - struct BSGrassShader_SetupTechnique - { - static bool thunk(RE::BSShader* shader, uint32_t globalTechnique) - { - if (globalTechnique == 0x5C000042) { - auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); - auto* graphicsState = RE::BSGraphics::State::GetSingleton(); - auto* renderer = RE::BSGraphics::Renderer::GetSingleton(); - - const uint32_t localTechnique = static_cast(SIE::ShaderCache::GrassShaderTechniques::TruePbr); - uint32_t shaderDescriptor = localTechnique; - if (graphicsState->useEarlyZ) { - shaderDescriptor |= static_cast(SIE::ShaderCache::GrassShaderFlags::AlphaTest); - } - - const bool began = hk_BSShader_BeginTechnique(shader, shaderDescriptor, shaderDescriptor, false); - if (!began) { - return false; - } - - static auto fogMethod = REL::Relocation(REL::RelocationID(100000, 106707)); - fogMethod(); - - static auto* bShadowsOnGrass = RE::GetINISetting("bShadowsOnGrass:Display"); - if (!bShadowsOnGrass->GetBool()) { - shadowState->SetPSTexture(1, graphicsState->defaultTextureWhite->rendererTexture); - shadowState->SetPSTextureAddressMode(1, RE::BSGraphics::TextureAddressMode::kClampSClampT); - shadowState->SetPSTextureFilterMode(1, RE::BSGraphics::TextureFilterMode::kNearest); - } else { - shadowState->SetPSTexture(1, renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGET::kSHADOW_MASK]); - shadowState->SetPSTextureAddressMode(1, RE::BSGraphics::TextureAddressMode::kClampSClampT); - - static auto* shadowMaskQuarter = RE::GetINISetting("iShadowMaskQuarter:Display"); - shadowState->SetPSTextureFilterMode(1, shadowMaskQuarter->GetSInt() != 4 ? RE::BSGraphics::TextureFilterMode::kBilinear : RE::BSGraphics::TextureFilterMode::kNearest); - } - - return true; - } - - return func(shader, globalTechnique); - }; - static inline REL::Relocation func; - }; - - struct BSGrassShader_SetupMaterial - { - static void thunk(RE::BSShader* shader, RE::BSLightingShaderMaterialBase const* material) - { - const auto& state = State::GetSingleton(); - const auto technique = static_cast(state->currentPixelDescriptor & 0b1111); - - if (technique == SIE::ShaderCache::GrassShaderTechniques::TruePbr) { - auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); - - RE::BSGraphics::Renderer::PreparePSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); - - auto* pbrMaterial = static_cast(material); - shadowState->SetPSTexture(0, pbrMaterial->diffuseTexture->rendererTexture); - shadowState->SetPSTextureAddressMode(0, static_cast(pbrMaterial->textureClampMode)); - shadowState->SetPSTextureFilterMode(0, RE::BSGraphics::TextureFilterMode::kAnisotropic); - - shadowState->SetPSTexture(2, pbrMaterial->normalTexture->rendererTexture); - shadowState->SetPSTextureAddressMode(2, static_cast(pbrMaterial->textureClampMode)); - shadowState->SetPSTextureFilterMode(2, RE::BSGraphics::TextureFilterMode::kAnisotropic); - - shadowState->SetPSTexture(3, pbrMaterial->rmaosTexture->rendererTexture); - shadowState->SetPSTextureAddressMode(3, static_cast(pbrMaterial->textureClampMode)); - shadowState->SetPSTextureFilterMode(3, RE::BSGraphics::TextureFilterMode::kAnisotropic); - - stl::enumeration shaderFlags; - if (pbrMaterial->pbrFlags.any(PBRFlags::Subsurface)) { - shaderFlags.set(PBRShaderFlags::Subsurface); - } - - const bool hasSubsurface = pbrMaterial->featuresTexture0 != nullptr && pbrMaterial->featuresTexture0 != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureWhite; - if (hasSubsurface) { - shadowState->SetPSTexture(4, pbrMaterial->featuresTexture0->rendererTexture); - shadowState->SetPSTextureAddressMode(4, static_cast(pbrMaterial->textureClampMode)); - shadowState->SetPSTextureFilterMode(4, RE::BSGraphics::TextureFilterMode::kAnisotropic); - - shaderFlags.set(PBRShaderFlags::HasFeaturesTexture0); - } - - { - shadowState->SetPSConstant(shaderFlags, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 0); - } - - { - std::array PBRParams1; - PBRParams1[0] = pbrMaterial->GetRoughnessScale(); - PBRParams1[1] = pbrMaterial->GetSpecularLevel(); - shadowState->SetPSConstant(PBRParams1, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 1); - } - - { - std::array PBRParams2; - PBRParams2[0] = pbrMaterial->GetSubsurfaceColor().red; - PBRParams2[1] = pbrMaterial->GetSubsurfaceColor().green; - PBRParams2[2] = pbrMaterial->GetSubsurfaceColor().blue; - PBRParams2[3] = pbrMaterial->GetSubsurfaceOpacity(); - shadowState->SetPSConstant(PBRParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 2); - } - - RE::BSGraphics::Renderer::FlushPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); - RE::BSGraphics::Renderer::ApplyPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); - } else { - func(shader, material); - } - }; - static inline REL::Relocation func; - }; - - struct TESBoundObject_Clone3D - { - static RE::NiAVObject* thunk(RE::TESBoundObject* object, RE::TESObjectREFR* ref, bool arg3) - { - auto* result = func(object, ref, arg3); - if (result != nullptr && ref != nullptr && ref->data.objectReference != nullptr && ref->data.objectReference->formType == RE::FormType::Static) { - auto* stat = static_cast(ref->data.objectReference); - if (stat->data.materialObj != nullptr && stat->data.materialObj->directionalData.singlePass) { - if (auto* pbrData = State::GetSingleton()->GetPBRMaterialObjectData(stat->data.materialObj)) { - RE::BSVisit::TraverseScenegraphGeometries(result, [pbrData](RE::BSGeometry* geometry) { - if (auto* shaderProperty = static_cast(geometry->GetGeometryRuntimeData().properties[1].get())) { - if (shaderProperty->GetMaterialType() == RE::BSShaderMaterial::Type::kLighting && - shaderProperty->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kVertexLighting)) { - if (auto* material = static_cast(shaderProperty->material)) { - material->projectedMaterialBaseColorScale = pbrData->baseColorScale; - material->projectedMaterialRoughness = pbrData->roughness; - material->projectedMaterialSpecularLevel = pbrData->specularLevel; - } - } - } - - return RE::BSVisit::BSVisitControl::kContinue; - }); - } - } - } - return result; - } - static inline REL::Relocation func; - }; - void Install() { SKSE::AllocTrampoline(14); @@ -1558,7 +491,7 @@ namespace Hooks logger::info("Hooking BSShader::LoadShaders"); *(uintptr_t*)&ptr_BSShader_LoadShaders = Detours::X64::DetourFunction(REL::RelocationID(101339, 108326).address(), (uintptr_t)&hk_BSShader_LoadShaders); logger::info("Hooking BSShader::BeginTechnique"); - *(uintptr_t*)&ptr_BSShader_BeginTechnique = Detours::X64::DetourFunction(REL::RelocationID(101341, 108328).address(), (uintptr_t)&hk_BSShader_BeginTechnique); + *(uintptr_t*)&ptr_BSShader_BeginTechnique = Detours::X64::DetourFunction(REL::RelocationID(101341, 108328).address(), (uintptr_t)&Hooks::hk_BSShader_BeginTechnique); stl::write_thunk_call(REL::RelocationID(101341, 108328).address() + REL::Relocate(0xC3, 0xD5)); stl::write_thunk_call(REL::RelocationID(101341, 108328).address() + REL::Relocate(0xD7, 0xEB)); @@ -1587,51 +520,5 @@ namespace Hooks stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0x406, 0x409)); stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0x1245, 0x123B, 0x1917)); stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0xA25, 0xA25, 0xCD2)); - - logger::info("Hooking BSLightingShaderProperty"); - stl::write_vfunc<0x18, BSLightingShaderProperty_LoadBinary>(RE::VTABLE_BSLightingShaderProperty[0]); - stl::write_vfunc<0x2A, BSLightingShaderProperty_GetRenderPasses>(RE::VTABLE_BSLightingShaderProperty[0]); - - logger::info("Hooking BSLightingShader"); - stl::write_vfunc<0x4, BSLightingShader_SetupMaterial>(RE::VTABLE_BSLightingShader[0]); - stl::write_vfunc<0x6, BSLightingShader_SetupGeometry>(RE::VTABLE_BSLightingShader[0]); - std::ignore = Detours::X64::DetourFunction(REL::RelocationID(101633, 108700).address(), (uintptr_t)&hk_BSLightingShader_GetPixelTechnique); - - logger::info("Hooking TESObjectLAND"); - *(uintptr_t*)&ptr_TESObjectLAND_SetupMaterial = Detours::X64::DetourFunction(REL::RelocationID(18368, 18791).address(), (uintptr_t)&hk_TESObjectLAND_SetupMaterial); - - logger::info("Hooking TESLandTexture"); - stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_TESLandTexture[0]); - stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_TESLandTexture[0]); - stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_BGSTextureSet[0]); - stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_BGSTextureSet[0]); - stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_BGSMaterialObject[0]); - stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_BGSMaterialObject[0]); - stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_BGSLightingTemplate[0]); - stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_BGSLightingTemplate[0]); - stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_TESWeather[0]); - stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_TESWeather[0]); - - logger::info("Hooking SetPerFrameBuffers"); - *(uintptr_t*)&ptr_SetPerFrameBuffers = Detours::X64::DetourFunction(REL::RelocationID(75570, 77371).address(), (uintptr_t)&hk_SetPerFrameBuffers); - - logger::info("Hooking BSTempEffectSimpleDecal"); - *(uintptr_t*)&ptr_BSTempEffectSimpleDecal_SetupGeometry = Detours::X64::DetourFunction(REL::RelocationID(29253, 30108).address(), (uintptr_t)&hk_BSTempEffectSimpleDecal_SetupGeometry); - - logger::info("Hooking BSTempEffectGeometryDecal"); - stl::write_vfunc<0x25, BSTempEffectGeometryDecal_Initialize>(RE::VTABLE_BSTempEffectGeometryDecal[0]); - - logger::info("Hooking BSGrassShaderProperty::ctor"); - stl::write_thunk_call(REL::RelocationID(15214, 15383).address() + REL::Relocate(0x45B, 0x4F5)); - - logger::info("Hooking BSGrassShaderProperty"); - stl::write_vfunc<0x2A, BSGrassShaderProperty_GetRenderPasses>(RE::VTABLE_BSGrassShaderProperty[0]); - - logger::info("Hooking BSGrassShader"); - stl::write_vfunc<0x2, BSGrassShader_SetupTechnique>(RE::VTABLE_BSGrassShader[0]); - stl::write_vfunc<0x4, BSGrassShader_SetupMaterial>(RE::VTABLE_BSGrassShader[0]); - - logger::info("Hooking TESObjectSTAT"); - stl::write_vfunc<0x4A, TESBoundObject_Clone3D>(RE::VTABLE_TESObjectSTAT[0]); } } \ No newline at end of file diff --git a/src/Hooks.h b/src/Hooks.h index 913cac0d2..d7b036a4a 100644 --- a/src/Hooks.h +++ b/src/Hooks.h @@ -2,5 +2,6 @@ namespace Hooks { + bool hk_BSShader_BeginTechnique(RE::BSShader* shader, uint32_t vertexDescriptor, uint32_t pixelDescriptor, bool skipPixelShader); void Install(); } diff --git a/src/Menu.cpp b/src/Menu.cpp index 946e17a16..e9489fa72 100644 --- a/src/Menu.cpp +++ b/src/Menu.cpp @@ -12,6 +12,7 @@ #include "Features/LightLimitFix/ParticleLights.h" #include "Deferred.h" +#include "TruePBR.h" #include "VariableRateShading.h" @@ -408,64 +409,7 @@ void Menu::DrawSettings() ImGui::TreePop(); } ImGui::Checkbox("Extended Frame Annotations", &State::GetSingleton()->extendedFrameAnnotations); - if (ImGui::TreeNodeEx("PBR", ImGuiTreeNodeFlags_DefaultOpen)) { - auto* state = State::GetSingleton(); - - if (const auto* player = RE::PlayerCharacter::GetSingleton()) { - if (const auto* currentCell = player->GetParentCell()) { - if (currentCell->IsInteriorCell()) { - if (const auto* lightingTemplate = currentCell->GetRuntimeData().lightingTemplate) { - if (ImGui::TreeNodeEx("Lighting Template Settings", ImGuiTreeNodeFlags_DefaultOpen)) { - const auto* editorId = lightingTemplate->GetFormEditorID(); - ImGui::Text(std::format("Current Lighting Template : {}", editorId).c_str()); - - auto& pbrData = state->pbrLightingTemplates[editorId]; - - ImGui::SliderFloat("Directional Light Scale", &pbrData.directionalLightColorScale, 0.f, 5.f); - ImGui::SliderFloat("Directional Ambient Light Scale", &pbrData.directionalAmbientLightColorScale, 0.f, 5.f); - - if (ImGui::Button("Save")) { - state->SavePBRLightingTemplateData(editorId); - } - - ImGui::TreePop(); - } - } - } else if (RE::Sky* sky = RE::Sky::GetSingleton()) { - if (const auto* weather = sky->currentWeather) { - if (ImGui::TreeNodeEx("Weather Settings", ImGuiTreeNodeFlags_DefaultOpen)) { - const auto* editorId = weather->GetFormEditorID(); - ImGui::Text(std::format("Current Weather : {}", editorId).c_str()); - - auto& pbrData = state->pbrWeathers[editorId]; - - ImGui::SliderFloat("Directional Light Scale", &pbrData.directionalLightColorScale, 0.f, 5.f); - ImGui::SliderFloat("Directional Ambient Light Scale", &pbrData.directionalAmbientLightColorScale, 0.f, 5.f); - - if (ImGui::Button("Save")) { - state->SavePBRWeatherData(editorId); - } - - ImGui::TreePop(); - } - } - } - } - } - - bool useMultipleScattering = state->pbrSettings.useMultipleScattering; - bool useMultiBounceAO = state->pbrSettings.useMultiBounceAO; - if (ImGui::Checkbox("Use Multiple Scattering", &useMultipleScattering)) { - state->pbrSettings.useMultipleScattering = useMultipleScattering; - } - if (ImGui::Checkbox("Use Multi-bounce AO", &useMultiBounceAO)) { - state->pbrSettings.useMultiBounceAO = useMultiBounceAO; - } - - ImGui::SliderFloat("Direct Light Color Multiplier", &state->globalPBRDirectLightColorMultiplier, 1e-3f, 1e2f, "%.3f", ImGuiSliderFlags_Logarithmic); - ImGui::SliderFloat("Ambient Light Color Multiplier", &state->globalPBRAmbientLightColorMultiplier, 1e-3f, 1e2f, "%.3f", ImGuiSliderFlags_Logarithmic); - ImGui::TreePop(); - } + TruePBR::GetSingleton()->DrawSettings(); } if (ImGui::CollapsingHeader("Replace Original Shaders", ImGuiTreeNodeFlags_OpenOnArrow | ImGuiTreeNodeFlags_OpenOnDoubleClick)) { diff --git a/src/State.cpp b/src/State.cpp index 8ba29d1a3..5f8236a44 100644 --- a/src/State.cpp +++ b/src/State.cpp @@ -12,87 +12,10 @@ #include "Deferred.h" #include "Features/Skylighting.h" #include "Features/TerrainBlending.h" +#include "TruePBR.h" #include "VariableRateShading.h" -namespace PNState -{ - template - bool Read(const json& config, ResultType& result) - { - if constexpr (std::is_same_v> || std::is_same_v) { - if (config.is_array() && config.size() == 3 && - config[0].is_number_float() && config[1].is_number_float() && - config[2].is_number_float()) { - result[0] = config[0]; - result[1] = config[1]; - result[2] = config[2]; - return true; - } - } - if constexpr (std::is_same_v) { - if (config.is_number_float()) { - result = config; - return true; - } - } - return false; - } - - void ReadPBRRecordConfigs(const std::string& rootPath, std::function recordReader) - { - if (std::filesystem::exists(rootPath)) { - auto configs = clib_util::distribution::get_configs(rootPath, "", ".json"); - - if (configs.empty()) { - logger::warn("[TruePBR] no .json files were found within the {} folder, aborting...", rootPath); - return; - } - - logger::info("[TruePBR] {} matching jsons found", configs.size()); - - for (auto& path : configs) { - logger::info("[TruePBR] loading json : {}", path); - - std::ifstream fileStream(path); - if (!fileStream.is_open()) { - logger::error("[TruePBR] failed to read {}", path); - continue; - } - - json config; - try { - fileStream >> config; - } catch (const nlohmann::json::parse_error& e) { - logger::error("[TruePBR] failed to parse {} : {}", path, e.what()); - continue; - } - - const auto editorId = std::filesystem::path(path).stem().string(); - recordReader(editorId, config); - } - } - } - - void SavePBRRecordConfig(const std::string& rootPath, const std::string& editorId, const json& config) - { - std::filesystem::create_directory(rootPath); - - const std::string outputPath = std::format("{}\\{}.json", rootPath, editorId); - std::ofstream fileStream(outputPath); - if (!fileStream.is_open()) { - logger::error("[TruePBR] failed to write {}", outputPath); - return; - } - try { - fileStream << std::setw(4) << config; - } catch (const nlohmann::json::type_error& e) { - logger::error("[TruePBR] failed to serialize {} : {}", outputPath, e.what()); - return; - } - } -} - void State::Draw() { const auto& shaderCache = SIE::ShaderCache::Instance(); @@ -101,6 +24,8 @@ void State::Draw() if (terrainBlending->loaded) terrainBlending->TerrainShaderHacks(); + TruePBR::GetSingleton()->SetShaderResouces(); + // auto skylighting = Skylighting::GetSingleton(); // if (skylighting->loaded) // skylighting->SkylightingShaderHacks(); @@ -164,10 +89,7 @@ void State::Reset() void State::Setup() { - SetupTextureSetData(); - SetupMaterialObjectData(); - SetupLightingTemplateData(); - SetupWeatherData(); + TruePBR::GetSingleton()->SetupResources(); SetupResources(); for (auto* feature : Feature::GetFeatureList()) if (feature->loaded) @@ -273,23 +195,10 @@ void State::Load(ConfigMode a_configMode) } } - if (settings["PBR"].is_object()) { - json& pbr = settings["PBR"]; - - if (pbr["Use Multiple Scattering"].is_boolean()) { - pbrSettings.useMultipleScattering = pbr["Use Multiple Scattering"]; - } - if (pbr["Use Multi-bounce AO"].is_boolean()) { - pbrSettings.useMultiBounceAO = pbr["Use Multi-bounce AO"]; - } - - if (pbr["Direct Light Color Multiplier"].is_number_float()) { - globalPBRDirectLightColorMultiplier = pbr["Direct Light Color Multiplier"]; - } - if (pbr["Ambient Light Color Multiplier"].is_number_float()) { - globalPBRAmbientLightColorMultiplier = pbr["Ambient Light Color Multiplier"]; - } - } + auto truePBR = TruePBR::GetSingleton(); + auto& pbrJson = settings[truePBR->GetShortName()]; + if (pbrJson.is_object()) + truePBR->LoadSettings(pbrJson); for (auto* feature : Feature::GetFeatureList()) feature->Load(settings); @@ -326,15 +235,9 @@ void State::Save(ConfigMode a_configMode) settings["General"] = general; - { - json pbr; - pbr["Use Multiple Scattering"] = pbrSettings.useMultipleScattering; - pbr["Use Multi-bounce AO"] = pbrSettings.useMultiBounceAO; - - pbr["Direct Light Color Multiplier"] = globalPBRDirectLightColorMultiplier; - pbr["Ambient Light Color Multiplier"] = globalPBRAmbientLightColorMultiplier; - settings["PBR"] = pbr; - } + auto truePBR = TruePBR::GetSingleton(); + auto& pbrJson = settings[truePBR->GetShortName()]; + truePBR->SaveSettings(pbrJson); json originalShaders; for (int classIndex = 0; classIndex < RE::BSShader::Type::Total - 1; ++classIndex) { @@ -359,6 +262,7 @@ void State::PostPostLoad() else logger::info("Skyrim Upscaler not detected"); Deferred::Hooks::Install(); + TruePBR::GetSingleton()->PostPostLoad(); } bool State::ValidateCache(CSimpleIniA& a_ini) @@ -630,207 +534,3 @@ void State::UpdateSharedData() context->PSSetShaderResources(20, 1, &srv); } - -void State::SetupFrame() -{ - float newDirectionalLightScale = 1.f; - float newDirectionalAmbientLightScale = 1.f; - - if (const auto* player = RE::PlayerCharacter::GetSingleton()) { - if (const auto* currentCell = player->GetParentCell()) { - if (currentCell->IsInteriorCell()) { - if (const auto* lightingTemplate = currentCell->GetRuntimeData().lightingTemplate) { - const auto* editorId = lightingTemplate->GetFormEditorID(); - if (auto it = pbrLightingTemplates.find(editorId); it != pbrLightingTemplates.cend()) { - newDirectionalLightScale = it->second.directionalLightColorScale; - newDirectionalAmbientLightScale = it->second.directionalAmbientLightColorScale; - } - } - } else if (RE::Sky* sky = RE::Sky::GetSingleton()) { - if (const auto* weather = sky->currentWeather) { - const auto* editorId = weather->GetFormEditorID(); - if (auto it = pbrWeathers.find(editorId); it != pbrWeathers.cend()) { - newDirectionalLightScale = it->second.directionalLightColorScale; - newDirectionalAmbientLightScale = it->second.directionalAmbientLightColorScale; - } - } - } - } - } - - weatherPBRDirectionalLightColorMultiplier = newDirectionalLightScale; - weatherPBRDirectionalAmbientLightColorMultiplier = newDirectionalAmbientLightScale; - - pbrSettings.directionalLightColorMultiplier = globalPBRDirectLightColorMultiplier * weatherPBRDirectionalLightColorMultiplier; - pbrSettings.pointLightColorMultiplier = globalPBRDirectLightColorMultiplier; - pbrSettings.ambientLightColorMultiplier = globalPBRAmbientLightColorMultiplier * weatherPBRDirectionalAmbientLightColorMultiplier; -} - -void State::SetupTextureSetData() -{ - logger::info("[TruePBR] loading PBR texture set configs"); - - pbrTextureSets.clear(); - - PNState::ReadPBRRecordConfigs("Data\\PBRTextureSets", [this](const std::string& editorId, const json& config) { - PBRTextureSetData textureSetData; - - PNState::Read(config["roughnessScale"], textureSetData.roughnessScale); - PNState::Read(config["displacementScale"], textureSetData.displacementScale); - PNState::Read(config["specularLevel"], textureSetData.specularLevel); - PNState::Read(config["subsurfaceColor"], textureSetData.subsurfaceColor); - PNState::Read(config["subsurfaceOpacity"], textureSetData.subsurfaceOpacity); - PNState::Read(config["coatColor"], textureSetData.coatColor); - PNState::Read(config["coatStrength"], textureSetData.coatStrength); - PNState::Read(config["coatRoughness"], textureSetData.coatRoughness); - PNState::Read(config["coatSpecularLevel"], textureSetData.coatSpecularLevel); - PNState::Read(config["innerLayerDisplacementOffset"], textureSetData.innerLayerDisplacementOffset); - PNState::Read(config["fuzzColor"], textureSetData.fuzzColor); - PNState::Read(config["fuzzWeight"], textureSetData.fuzzWeight); - - pbrTextureSets.insert_or_assign(editorId, textureSetData); - }); -} - -State::PBRTextureSetData* State::GetPBRTextureSetData(const RE::TESForm* textureSet) -{ - if (textureSet == nullptr) { - return nullptr; - } - - auto it = pbrTextureSets.find(textureSet->GetFormEditorID()); - if (it == pbrTextureSets.end()) { - return nullptr; - } - return &it->second; -} - -bool State::IsPBRTextureSet(const RE::TESForm* textureSet) -{ - return GetPBRTextureSetData(textureSet) != nullptr; -} - -void State::SetupMaterialObjectData() -{ - logger::info("[TruePBR] loading PBR material object configs"); - - pbrMaterialObjects.clear(); - - PNState::ReadPBRRecordConfigs("Data\\PBRMaterialObjects", [this](const std::string& editorId, const json& config) { - PBRMaterialObjectData materialObjectData; - - PNState::Read(config["baseColorScale"], materialObjectData.baseColorScale); - PNState::Read(config["roughness"], materialObjectData.roughness); - PNState::Read(config["specularLevel"], materialObjectData.specularLevel); - - pbrMaterialObjects.insert_or_assign(editorId, materialObjectData); - }); -} - -State::PBRMaterialObjectData* State::GetPBRMaterialObjectData(const RE::TESForm* materialObject) -{ - if (materialObject == nullptr) { - return nullptr; - } - - auto it = pbrMaterialObjects.find(materialObject->GetFormEditorID()); - if (it == pbrMaterialObjects.end()) { - return nullptr; - } - return &it->second; -} - -bool State::IsPBRMaterialObject(const RE::TESForm* materialObject) -{ - return GetPBRMaterialObjectData(materialObject) != nullptr; -} - -void State::SetupLightingTemplateData() -{ - logger::info("[TruePBR] loading PBR lighting template configs"); - - pbrLightingTemplates.clear(); - - PNState::ReadPBRRecordConfigs("Data\\PBRLightingTemplates", [this](const std::string& editorId, const json& config) { - PBRLightingTemplateData lightingTemplateData; - - PNState::Read(config["directionalLightColorScale"], lightingTemplateData.directionalLightColorScale); - PNState::Read(config["directionalAmbientLightColorScale"], lightingTemplateData.directionalAmbientLightColorScale); - - pbrLightingTemplates.insert_or_assign(editorId, lightingTemplateData); - }); -} - -State::PBRLightingTemplateData* State::GetPBRLightingTemplateData(const RE::TESForm* lightingTemplate) -{ - if (lightingTemplate == nullptr) { - return nullptr; - } - - auto it = pbrLightingTemplates.find(lightingTemplate->GetFormEditorID()); - if (it == pbrLightingTemplates.end()) { - return nullptr; - } - return &it->second; -} - -bool State::IsPBRLightingTemplate(const RE::TESForm* lightingTemplate) -{ - return GetPBRLightingTemplateData(lightingTemplate) != nullptr; -} - -void State::SavePBRLightingTemplateData(const std::string& editorId) -{ - const auto& pbrLightingTemplateData = pbrLightingTemplates[editorId]; - - json config; - config["directionalLightColorScale"] = pbrLightingTemplateData.directionalLightColorScale; - config["directionalAmbientLightColorScale"] = pbrLightingTemplateData.directionalAmbientLightColorScale; - - PNState::SavePBRRecordConfig("Data\\PBRLightingTemplates\\", editorId, config); -} - -void State::SetupWeatherData() -{ - logger::info("[TruePBR] loading PBR weather configs"); - - pbrWeathers.clear(); - - PNState::ReadPBRRecordConfigs("Data\\PBRWeathers", [this](const std::string& editorId, const json& config) { - PBRWeatherData weatherData; - - PNState::Read(config["directionalLightColorScale"], weatherData.directionalLightColorScale); - PNState::Read(config["directionalAmbientLightColorScale"], weatherData.directionalAmbientLightColorScale); - - pbrWeathers.insert_or_assign(editorId, weatherData); - }); -} - -State::PBRWeatherData* State::GetPBRWeatherData(const RE::TESForm* weather) -{ - if (weather == nullptr) { - return nullptr; - } - - auto it = pbrWeathers.find(weather->GetFormEditorID()); - if (it == pbrWeathers.end()) { - return nullptr; - } - return &it->second; -} - -bool State::IsPBRWeather(const RE::TESForm* weather) -{ - return GetPBRWeatherData(weather) != nullptr; -} - -void State::SavePBRWeatherData(const std::string& editorId) -{ - const auto& pbrWeatherData = pbrWeathers[editorId]; - - json config; - config["directionalLightColorScale"] = pbrWeatherData.directionalLightColorScale; - config["directionalAmbientLightColorScale"] = pbrWeatherData.directionalAmbientLightColorScale; - - PNState::SavePBRRecordConfig("Data\\PBRWeathers\\", editorId, config); -} diff --git a/src/State.h b/src/State.h index 3743060eb..370da213e 100644 --- a/src/State.h +++ b/src/State.h @@ -46,7 +46,6 @@ class State void Draw(); void Reset(); void Setup(); - void SetupFrame(); void Load(ConfigMode a_configMode = ConfigMode::USER); void Save(ConfigMode a_configMode = ConfigMode::USER); @@ -140,90 +139,6 @@ class State ID3D11DeviceContext* context = nullptr; ID3D11Device* device = nullptr; - std::unordered_map editorIDs; - - float globalPBRDirectLightColorMultiplier = 1.f; - float globalPBRAmbientLightColorMultiplier = 1.f; - - float weatherPBRDirectionalLightColorMultiplier = 1.f; - float weatherPBRDirectionalAmbientLightColorMultiplier = 1.f; - -#pragma warning(push) -#pragma warning(disable: 4324) - struct alignas(16) PBRSettings - { - float directionalLightColorMultiplier = 1.f; - float pointLightColorMultiplier = 1.f; - float ambientLightColorMultiplier = 1.f; - uint32_t useMultipleScattering = true; - uint32_t useMultiBounceAO = true; - } pbrSettings{}; -#pragma warning(pop) - - struct PBRTextureSetData - { - float roughnessScale = 1.f; - float displacementScale = 1.f; - float specularLevel = 0.04f; - - RE::NiColor subsurfaceColor; - float subsurfaceOpacity = 0.f; - - RE::NiColor coatColor = { 1.f, 1.f, 1.f }; - float coatStrength = 1.f; - float coatRoughness = 1.f; - float coatSpecularLevel = 0.04f; - float innerLayerDisplacementOffset = 0.f; - - RE::NiColor fuzzColor; - float fuzzWeight = 0.f; - }; - - void SetupTextureSetData(); - State::PBRTextureSetData* GetPBRTextureSetData(const RE::TESForm* textureSet); - bool IsPBRTextureSet(const RE::TESForm* textureSet); - - std::unordered_map pbrTextureSets; - - struct PBRMaterialObjectData - { - std::array baseColorScale = { 1.f, 1.f, 1.f }; - float roughness = 1.f; - float specularLevel = 1.f; - }; - - void SetupMaterialObjectData(); - State::PBRMaterialObjectData* GetPBRMaterialObjectData(const RE::TESForm* materialObject); - bool IsPBRMaterialObject(const RE::TESForm* materialObject); - - std::unordered_map pbrMaterialObjects; - - struct PBRLightingTemplateData - { - float directionalLightColorScale = 1.f; - float directionalAmbientLightColorScale = 1.f; - }; - - void SetupLightingTemplateData(); - State::PBRLightingTemplateData* GetPBRLightingTemplateData(const RE::TESForm* lightingTemplate); - bool IsPBRLightingTemplate(const RE::TESForm* lightingTemplate); - void SavePBRLightingTemplateData(const std::string& editorId); - - std::unordered_map pbrLightingTemplates; - - struct PBRWeatherData - { - float directionalLightColorScale = 1.f; - float directionalAmbientLightColorScale = 1.f; - }; - - void SetupWeatherData(); - State::PBRWeatherData* GetPBRWeatherData(const RE::TESForm* weather); - bool IsPBRWeather(const RE::TESForm* weather); - void SavePBRWeatherData(const std::string& editorId); - - std::unordered_map pbrWeathers; - private: std::shared_ptr pPerf; bool initialized = false; diff --git a/src/TruePBR.cpp b/src/TruePBR.cpp new file mode 100644 index 000000000..51a952b44 --- /dev/null +++ b/src/TruePBR.cpp @@ -0,0 +1,1505 @@ +#include "TruePBR.h" + +#include + +#include "TruePBR/BSLightingShaderMaterialPBR.h" +#include "TruePBR/BSLightingShaderMaterialPBRLandscape.h" + +#include "Hooks.h" +#include "ShaderCache.h" +#include "State.h" + +namespace PNState +{ + template + bool Read(const json& config, ResultType& result) + { + if constexpr (std::is_same_v> || std::is_same_v) { + if (config.is_array() && config.size() == 3 && + config[0].is_number_float() && config[1].is_number_float() && + config[2].is_number_float()) { + result[0] = config[0]; + result[1] = config[1]; + result[2] = config[2]; + return true; + } + } + if constexpr (std::is_same_v) { + if (config.is_number_float()) { + result = config; + return true; + } + } + return false; + } + + void ReadPBRRecordConfigs(const std::string& rootPath, std::function recordReader) + { + if (std::filesystem::exists(rootPath)) { + auto configs = clib_util::distribution::get_configs(rootPath, "", ".json"); + + if (configs.empty()) { + logger::warn("[TruePBR] no .json files were found within the {} folder, aborting...", rootPath); + return; + } + + logger::info("[TruePBR] {} matching jsons found", configs.size()); + + for (auto& path : configs) { + logger::info("[TruePBR] loading json : {}", path); + + std::ifstream fileStream(path); + if (!fileStream.is_open()) { + logger::error("[TruePBR] failed to read {}", path); + continue; + } + + json config; + try { + fileStream >> config; + } catch (const nlohmann::json::parse_error& e) { + logger::error("[TruePBR] failed to parse {} : {}", path, e.what()); + continue; + } + + const auto editorId = std::filesystem::path(path).stem().string(); + recordReader(editorId, config); + } + } + } + + void SavePBRRecordConfig(const std::string& rootPath, const std::string& editorId, const json& config) + { + std::filesystem::create_directory(rootPath); + + const std::string outputPath = std::format("{}\\{}.json", rootPath, editorId); + std::ofstream fileStream(outputPath); + if (!fileStream.is_open()) { + logger::error("[TruePBR] failed to write {}", outputPath); + return; + } + try { + fileStream << std::setw(4) << config; + } catch (const nlohmann::json::type_error& e) { + logger::error("[TruePBR] failed to serialize {} : {}", outputPath, e.what()); + return; + } + } +} + +void TruePBR::DrawSettings() +{ + if (ImGui::TreeNodeEx("PBR", ImGuiTreeNodeFlags_DefaultOpen)) { + if (const auto* player = RE::PlayerCharacter::GetSingleton()) { + if (const auto* currentCell = player->GetParentCell()) { + if (currentCell->IsInteriorCell()) { + if (const auto* lightingTemplate = currentCell->GetRuntimeData().lightingTemplate) { + if (ImGui::TreeNodeEx("Lighting Template Settings", ImGuiTreeNodeFlags_DefaultOpen)) { + const auto* editorId = lightingTemplate->GetFormEditorID(); + ImGui::Text(std::format("Current Lighting Template : {}", editorId).c_str()); + + auto& pbrData = pbrLightingTemplates[editorId]; + + ImGui::SliderFloat("Directional Light Scale", &pbrData.directionalLightColorScale, 0.f, 5.f); + ImGui::SliderFloat("Directional Ambient Light Scale", &pbrData.directionalAmbientLightColorScale, 0.f, 5.f); + + if (ImGui::Button("Save")) { + SavePBRLightingTemplateData(editorId); + } + + ImGui::TreePop(); + } + } + } else if (RE::Sky* sky = RE::Sky::GetSingleton()) { + if (const auto* weather = sky->currentWeather) { + if (ImGui::TreeNodeEx("Weather Settings", ImGuiTreeNodeFlags_DefaultOpen)) { + const auto* editorId = weather->GetFormEditorID(); + ImGui::Text(std::format("Current Weather : {}", editorId).c_str()); + + auto& pbrData = pbrWeathers[editorId]; + + ImGui::SliderFloat("Directional Light Scale", &pbrData.directionalLightColorScale, 0.f, 5.f); + ImGui::SliderFloat("Directional Ambient Light Scale", &pbrData.directionalAmbientLightColorScale, 0.f, 5.f); + + if (ImGui::Button("Save")) { + SavePBRWeatherData(editorId); + } + + ImGui::TreePop(); + } + } + } + } + } + + bool useMultipleScattering = settings.useMultipleScattering; + bool useMultiBounceAO = settings.useMultiBounceAO; + if (ImGui::Checkbox("Use Multiple Scattering", &useMultipleScattering)) { + settings.useMultipleScattering = useMultipleScattering; + } + if (ImGui::Checkbox("Use Multi-bounce AO", &useMultiBounceAO)) { + settings.useMultiBounceAO = useMultiBounceAO; + } + + ImGui::SliderFloat("Direct Light Color Multiplier", &globalPBRDirectLightColorMultiplier, 1e-3f, 1e2f, "%.3f", ImGuiSliderFlags_Logarithmic); + ImGui::SliderFloat("Ambient Light Color Multiplier", &globalPBRAmbientLightColorMultiplier, 1e-3f, 1e2f, "%.3f", ImGuiSliderFlags_Logarithmic); + ImGui::TreePop(); + } +} + +void TruePBR::SetupResources() +{ + SetupTextureSetData(); + SetupMaterialObjectData(); + SetupLightingTemplateData(); + SetupWeatherData(); +} + +void TruePBR::LoadSettings(json& o_json) +{ + if (o_json["Use Multiple Scattering"].is_boolean()) { + settings.useMultipleScattering = o_json["Use Multiple Scattering"]; + } + if (o_json["Use Multi-bounce AO"].is_boolean()) { + settings.useMultiBounceAO = o_json["Use Multi-bounce AO"]; + } + + if (o_json["Direct Light Color Multiplier"].is_number_float()) { + globalPBRDirectLightColorMultiplier = o_json["Direct Light Color Multiplier"]; + } + if (o_json["Ambient Light Color Multiplier"].is_number_float()) { + globalPBRAmbientLightColorMultiplier = o_json["Ambient Light Color Multiplier"]; + } +} + +void TruePBR::SaveSettings(json& o_json) +{ + o_json["Use Multiple Scattering"] = (bool)settings.useMultipleScattering; + o_json["Use Multi-bounce AO"] = (bool)settings.useMultiBounceAO; + + o_json["Direct Light Color Multiplier"] = globalPBRDirectLightColorMultiplier; + o_json["Ambient Light Color Multiplier"] = globalPBRAmbientLightColorMultiplier; +} + +void TruePBR::SetupFrame() +{ + float newDirectionalLightScale = 1.f; + float newDirectionalAmbientLightScale = 1.f; + + if (const auto* player = RE::PlayerCharacter::GetSingleton()) { + if (const auto* currentCell = player->GetParentCell()) { + if (currentCell->IsInteriorCell()) { + if (const auto* lightingTemplate = currentCell->GetRuntimeData().lightingTemplate) { + const auto* editorId = lightingTemplate->GetFormEditorID(); + if (auto it = pbrLightingTemplates.find(editorId); it != pbrLightingTemplates.cend()) { + newDirectionalLightScale = it->second.directionalLightColorScale; + newDirectionalAmbientLightScale = it->second.directionalAmbientLightColorScale; + } + } + } else if (RE::Sky* sky = RE::Sky::GetSingleton()) { + if (const auto* weather = sky->currentWeather) { + const auto* editorId = weather->GetFormEditorID(); + if (auto it = pbrWeathers.find(editorId); it != pbrWeathers.cend()) { + newDirectionalLightScale = it->second.directionalLightColorScale; + newDirectionalAmbientLightScale = it->second.directionalAmbientLightColorScale; + } + } + } + } + } + + weatherPBRDirectionalLightColorMultiplier = newDirectionalLightScale; + weatherPBRDirectionalAmbientLightColorMultiplier = newDirectionalAmbientLightScale; + + settings.directionalLightColorMultiplier = globalPBRDirectLightColorMultiplier * weatherPBRDirectionalLightColorMultiplier; + settings.pointLightColorMultiplier = globalPBRDirectLightColorMultiplier; + settings.ambientLightColorMultiplier = globalPBRAmbientLightColorMultiplier * weatherPBRDirectionalAmbientLightColorMultiplier; +} + +void TruePBR::SetupTextureSetData() +{ + logger::info("[TruePBR] loading PBR texture set configs"); + + pbrTextureSets.clear(); + + PNState::ReadPBRRecordConfigs("Data\\PBRTextureSets", [this](const std::string& editorId, const json& config) { + PBRTextureSetData textureSetData; + + PNState::Read(config["roughnessScale"], textureSetData.roughnessScale); + PNState::Read(config["displacementScale"], textureSetData.displacementScale); + PNState::Read(config["specularLevel"], textureSetData.specularLevel); + PNState::Read(config["subsurfaceColor"], textureSetData.subsurfaceColor); + PNState::Read(config["subsurfaceOpacity"], textureSetData.subsurfaceOpacity); + PNState::Read(config["coatColor"], textureSetData.coatColor); + PNState::Read(config["coatStrength"], textureSetData.coatStrength); + PNState::Read(config["coatRoughness"], textureSetData.coatRoughness); + PNState::Read(config["coatSpecularLevel"], textureSetData.coatSpecularLevel); + PNState::Read(config["innerLayerDisplacementOffset"], textureSetData.innerLayerDisplacementOffset); + PNState::Read(config["fuzzColor"], textureSetData.fuzzColor); + PNState::Read(config["fuzzWeight"], textureSetData.fuzzWeight); + + pbrTextureSets.insert_or_assign(editorId, textureSetData); + }); +} + +TruePBR::PBRTextureSetData* TruePBR::GetPBRTextureSetData(const RE::TESForm* textureSet) +{ + if (textureSet == nullptr) { + return nullptr; + } + + auto it = pbrTextureSets.find(textureSet->GetFormEditorID()); + if (it == pbrTextureSets.end()) { + return nullptr; + } + return &it->second; +} + +bool TruePBR::IsPBRTextureSet(const RE::TESForm* textureSet) +{ + return GetPBRTextureSetData(textureSet) != nullptr; +} + +void TruePBR::SetupMaterialObjectData() +{ + logger::info("[TruePBR] loading PBR material object configs"); + + pbrMaterialObjects.clear(); + + PNState::ReadPBRRecordConfigs("Data\\PBRMaterialObjects", [this](const std::string& editorId, const json& config) { + PBRMaterialObjectData materialObjectData; + + PNState::Read(config["baseColorScale"], materialObjectData.baseColorScale); + PNState::Read(config["roughness"], materialObjectData.roughness); + PNState::Read(config["specularLevel"], materialObjectData.specularLevel); + + pbrMaterialObjects.insert_or_assign(editorId, materialObjectData); + }); +} + +TruePBR::PBRMaterialObjectData* TruePBR::GetPBRMaterialObjectData(const RE::TESForm* materialObject) +{ + if (materialObject == nullptr) { + return nullptr; + } + + auto it = pbrMaterialObjects.find(materialObject->GetFormEditorID()); + if (it == pbrMaterialObjects.end()) { + return nullptr; + } + return &it->second; +} + +bool TruePBR::IsPBRMaterialObject(const RE::TESForm* materialObject) +{ + return GetPBRMaterialObjectData(materialObject) != nullptr; +} + +void TruePBR::SetupLightingTemplateData() +{ + logger::info("[TruePBR] loading PBR lighting template configs"); + + pbrLightingTemplates.clear(); + + PNState::ReadPBRRecordConfigs("Data\\PBRLightingTemplates", [this](const std::string& editorId, const json& config) { + PBRLightingTemplateData lightingTemplateData; + + PNState::Read(config["directionalLightColorScale"], lightingTemplateData.directionalLightColorScale); + PNState::Read(config["directionalAmbientLightColorScale"], lightingTemplateData.directionalAmbientLightColorScale); + + pbrLightingTemplates.insert_or_assign(editorId, lightingTemplateData); + }); +} + +TruePBR::PBRLightingTemplateData* TruePBR::GetPBRLightingTemplateData(const RE::TESForm* lightingTemplate) +{ + if (lightingTemplate == nullptr) { + return nullptr; + } + + auto it = pbrLightingTemplates.find(lightingTemplate->GetFormEditorID()); + if (it == pbrLightingTemplates.end()) { + return nullptr; + } + return &it->second; +} + +bool TruePBR::IsPBRLightingTemplate(const RE::TESForm* lightingTemplate) +{ + return GetPBRLightingTemplateData(lightingTemplate) != nullptr; +} + +void TruePBR::SavePBRLightingTemplateData(const std::string& editorId) +{ + const auto& pbrLightingTemplateData = pbrLightingTemplates[editorId]; + + json config; + config["directionalLightColorScale"] = pbrLightingTemplateData.directionalLightColorScale; + config["directionalAmbientLightColorScale"] = pbrLightingTemplateData.directionalAmbientLightColorScale; + + PNState::SavePBRRecordConfig("Data\\PBRLightingTemplates\\", editorId, config); +} + +void TruePBR::SetupWeatherData() +{ + logger::info("[TruePBR] loading PBR weather configs"); + + pbrWeathers.clear(); + + PNState::ReadPBRRecordConfigs("Data\\PBRWeathers", [this](const std::string& editorId, const json& config) { + PBRWeatherData weatherData; + + PNState::Read(config["directionalLightColorScale"], weatherData.directionalLightColorScale); + PNState::Read(config["directionalAmbientLightColorScale"], weatherData.directionalAmbientLightColorScale); + + pbrWeathers.insert_or_assign(editorId, weatherData); + }); +} + +TruePBR::PBRWeatherData* TruePBR::GetPBRWeatherData(const RE::TESForm* weather) +{ + if (weather == nullptr) { + return nullptr; + } + + auto it = pbrWeathers.find(weather->GetFormEditorID()); + if (it == pbrWeathers.end()) { + return nullptr; + } + return &it->second; +} + +bool TruePBR::IsPBRWeather(const RE::TESForm* weather) +{ + return GetPBRWeatherData(weather) != nullptr; +} + +void TruePBR::SavePBRWeatherData(const std::string& editorId) +{ + const auto& pbrWeatherData = pbrWeathers[editorId]; + + json config; + config["directionalLightColorScale"] = pbrWeatherData.directionalLightColorScale; + config["directionalAmbientLightColorScale"] = pbrWeatherData.directionalAmbientLightColorScale; + + PNState::SavePBRRecordConfig("Data\\PBRWeathers\\", editorId, config); +} + +namespace Permutations +{ + template + std::unordered_set GenerateFlagPermutations(const RangeType& flags, uint32_t constantFlags) + { + std::vector flagValues; + std::ranges::transform(flags, std::back_inserter(flagValues), [](auto flag) { return static_cast(flag); }); + const uint32_t size = static_cast(flagValues.size()); + + std::unordered_set result; + for (uint32_t mask = 0; mask < (1u << size); ++mask) { + uint32_t flag = constantFlags; + for (size_t index = 0; index < size; ++index) { + if (mask & (1 << index)) { + flag |= flagValues[index]; + } + } + result.insert(flag); + } + + return result; + } + + uint32_t GetLightingShaderDescriptor(SIE::ShaderCache::LightingShaderTechniques technique, uint32_t flags) + { + return ((static_cast(technique) & 0x3F) << 24) | flags; + } + + void AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques technique, const std::unordered_set& flags, std::unordered_set& result) + { + for (uint32_t flag : flags) { + result.insert(GetLightingShaderDescriptor(technique, flag)); + } + } + + std::unordered_set GeneratePBRLightingVertexPermutations() + { + using enum SIE::ShaderCache::LightingShaderFlags; + + constexpr std::array defaultFlags{ VC, Skinned, WorldMap }; + constexpr std::array projectedUvFlags{ VC, WorldMap }; + constexpr std::array treeFlags{ VC, Skinned }; + constexpr std::array landFlags{ VC }; + + constexpr uint32_t defaultConstantFlags = static_cast(TruePbr); + constexpr uint32_t projectedUvConstantFlags = static_cast(TruePbr) | static_cast(ProjectedUV); + + const std::unordered_set defaultFlagValues = GenerateFlagPermutations(defaultFlags, defaultConstantFlags); + const std::unordered_set projectedUvFlagValues = GenerateFlagPermutations(projectedUvFlags, projectedUvConstantFlags); + const std::unordered_set treeFlagValues = GenerateFlagPermutations(treeFlags, defaultConstantFlags); + const std::unordered_set landFlagValues = GenerateFlagPermutations(landFlags, defaultConstantFlags); + + std::unordered_set result; + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::None, defaultFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::None, projectedUvFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::TreeAnim, treeFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLand, landFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLandLODBlend, landFlagValues, result); + return result; + } + + std::unordered_set GeneratePBRLightingPixelPermutations() + { + using enum SIE::ShaderCache::LightingShaderFlags; + + constexpr std::array defaultFlags{ Skinned, DoAlphaTest, AdditionalAlphaMask }; + constexpr std::array projectedUvFlags{ DoAlphaTest, AdditionalAlphaMask, Snow, BaseObjectIsSnow }; + constexpr std::array lodObjectsFlags{ WorldMap, DoAlphaTest, AdditionalAlphaMask, ProjectedUV }; + constexpr std::array treeFlags{ Skinned, DoAlphaTest, AdditionalAlphaMask }; + + constexpr uint32_t defaultConstantFlags = static_cast(TruePbr) | static_cast(VC); + constexpr uint32_t projectedUvConstantFlags = static_cast(TruePbr) | static_cast(VC) | static_cast(ProjectedUV); + + const std::unordered_set defaultFlagValues = GenerateFlagPermutations(defaultFlags, defaultConstantFlags); + const std::unordered_set projectedUvFlagValues = GenerateFlagPermutations(projectedUvFlags, projectedUvConstantFlags); + const std::unordered_set lodObjectsFlagValues = GenerateFlagPermutations(lodObjectsFlags, defaultConstantFlags); + const std::unordered_set treeFlagValues = GenerateFlagPermutations(treeFlags, defaultConstantFlags); + const std::unordered_set landFlagValues = { defaultConstantFlags }; + + std::unordered_set result; + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::None, defaultFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::None, projectedUvFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::LODObjects, lodObjectsFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::LODObjectHD, lodObjectsFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::TreeAnim, treeFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLand, landFlagValues, result); + AddLightingShaderDescriptors(SIE::ShaderCache::LightingShaderTechniques::MTLandLODBlend, landFlagValues, result); + return result; + } + + std::unordered_set GeneratePBRGrassPermutations() + { + using enum SIE::ShaderCache::GrassShaderTechniques; + using enum SIE::ShaderCache::GrassShaderFlags; + + return { static_cast(TruePbr), + static_cast(TruePbr) | static_cast(AlphaTest) }; + } + + std::unordered_set GeneratePBRGrassVertexPermutations() + { + return GeneratePBRGrassPermutations(); + } + + std::unordered_set GeneratePBRGrassPixelPermutations() + { + return GeneratePBRGrassPermutations(); + } +} + +void TruePBR::GenerateShaderPermutations(RE::BSShader* shader) +{ + auto& shaderCache = SIE::ShaderCache::Instance(); + if (shader->shaderType == RE::BSShader::Type::Lighting) { + const auto vertexPermutations = Permutations::GeneratePBRLightingVertexPermutations(); + for (auto descriptor : vertexPermutations) { + auto vertexShaderDesriptor = descriptor; + auto pixelShaderDescriptor = descriptor; + State::GetSingleton()->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor); + std::ignore = shaderCache.GetVertexShader(*shader, vertexShaderDesriptor); + } + + const auto pixelPermutations = Permutations::GeneratePBRLightingPixelPermutations(); + for (auto descriptor : pixelPermutations) { + auto vertexShaderDesriptor = descriptor; + auto pixelShaderDescriptor = descriptor; + State::GetSingleton()->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor); + std::ignore = shaderCache.GetPixelShader(*shader, pixelShaderDescriptor); + } + } else if (shader->shaderType == RE::BSShader::Type::Grass) { + const auto vertexPermutations = Permutations::GeneratePBRGrassVertexPermutations(); + for (auto descriptor : vertexPermutations) { + auto vertexShaderDesriptor = descriptor; + auto pixelShaderDescriptor = descriptor; + State::GetSingleton()->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor); + std::ignore = shaderCache.GetVertexShader(*shader, vertexShaderDesriptor); + } + + const auto pixelPermutations = Permutations::GeneratePBRGrassPixelPermutations(); + for (auto descriptor : pixelPermutations) { + auto vertexShaderDesriptor = descriptor; + auto pixelShaderDescriptor = descriptor; + State::GetSingleton()->ModifyShaderLookup(*shader, vertexShaderDesriptor, pixelShaderDescriptor); + std::ignore = shaderCache.GetPixelShader(*shader, pixelShaderDescriptor); + } + } +} + +struct ExtendedRendererState +{ + static constexpr uint32_t NumPSTextures = 12; + static constexpr uint32_t FirstPSTexture = 80; + + uint32_t PSResourceModifiedBits = 0; + ID3D11ShaderResourceView* PSTexture[NumPSTextures]; + + void SetPSTexture(size_t textureIndex, RE::BSGraphics::Texture* newTexture) + { + ID3D11ShaderResourceView* resourceView = newTexture ? newTexture->resourceView : nullptr; + //if (PSTexture[textureIndex] != resourceView) + { + PSTexture[textureIndex] = resourceView; + PSResourceModifiedBits |= (1 << textureIndex); + } + } + + ExtendedRendererState() + { + std::fill_n(PSTexture, NumPSTextures, nullptr); + } +} extendedRendererState; + +struct BSLightingShaderProperty_LoadBinary +{ + static void thunk(RE::BSLightingShaderProperty* property, RE::NiStream& stream) + { + using enum RE::BSShaderProperty::EShaderPropertyFlag; + + RE::BSShaderMaterial::Feature feature = RE::BSShaderMaterial::Feature::kDefault; + stream.iStr->read(&feature, 1); + + { + auto vtable = REL::Relocation(RE::NiShadeProperty::VTABLE[0]); + auto baseMethod = reinterpret_cast((vtable.get()[0x18])); + baseMethod(property, stream); + } + + stream.iStr->read(&property->flags, 1); + + bool isPbr = false; + { + RE::BSLightingShaderMaterialBase* material = nullptr; + if (property->flags.any(kMenuScreen)) { + auto* pbrMaterial = BSLightingShaderMaterialPBR::Make(); + pbrMaterial->loadedWithFeature = feature; + material = pbrMaterial; + isPbr = true; + } else { + material = RE::BSLightingShaderMaterialBase::CreateMaterial(feature); + } + property->LinkMaterial(nullptr, false); + property->material = material; + } + + { + stream.iStr->read(&property->material->texCoordOffset[0].x, 1); + stream.iStr->read(&property->material->texCoordOffset[0].y, 1); + stream.iStr->read(&property->material->texCoordScale[0].x, 1); + stream.iStr->read(&property->material->texCoordScale[0].y, 1); + + property->material->texCoordOffset[1] = property->material->texCoordOffset[0]; + property->material->texCoordScale[1] = property->material->texCoordScale[0]; + } + + stream.LoadLinkID(); + + { + RE::NiColor emissiveColor{}; + stream.iStr->read(&emissiveColor.red, 1); + stream.iStr->read(&emissiveColor.green, 1); + stream.iStr->read(&emissiveColor.blue, 1); + + if (property->emissiveColor != nullptr && property->flags.any(kOwnEmit)) { + *property->emissiveColor = emissiveColor; + } + } + + stream.iStr->read(&property->emissiveMult, 1); + + static_cast(property->material)->LoadBinary(stream); + + if (isPbr) { + auto pbrMaterial = static_cast(property->material); + if (property->flags.any(kMultiLayerParallax)) { + pbrMaterial->pbrFlags.set(PBRFlags::TwoLayer); + if (property->flags.any(kSoftLighting)) { + pbrMaterial->pbrFlags.set(PBRFlags::InterlayerParallax); + } + if (property->flags.any(kBackLighting)) { + pbrMaterial->pbrFlags.set(PBRFlags::CoatNormal); + } + if (property->flags.any(kEffectLighting)) { + pbrMaterial->pbrFlags.set(PBRFlags::ColoredCoat); + } + } else if (property->flags.any(kBackLighting)) { + pbrMaterial->pbrFlags.set(PBRFlags::HairMarschner); + } else { + if (property->flags.any(kRimLighting)) { + pbrMaterial->pbrFlags.set(PBRFlags::Subsurface); + } + if (property->flags.any(kSoftLighting)) { + pbrMaterial->pbrFlags.set(PBRFlags::Fuzz); + } + } + property->flags.set(kVertexLighting); + property->flags.reset(kMenuScreen, kSpecular, kGlowMap, kEnvMap, kMultiLayerParallax, kSoftLighting, kRimLighting, kBackLighting, kAnisotropicLighting, kEffectLighting); + } + } + static inline REL::Relocation func; +}; + +struct BSLightingShaderProperty_GetRenderPasses +{ + static RE::BSShaderProperty::RenderPassArray* thunk(RE::BSLightingShaderProperty* property, RE::BSGeometry* geometry, std::uint32_t renderFlags, RE::BSShaderAccumulator* accumulator) + { + auto renderPasses = func(property, geometry, renderFlags, accumulator); + if (renderPasses == nullptr) { + return renderPasses; + } + + bool isPbr = false; + + if (property->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kVertexLighting) && (property->material->GetFeature() == RE::BSShaderMaterial::Feature::kDefault || property->material->GetFeature() == RE::BSShaderMaterial::Feature::kMultiTexLandLODBlend)) { + isPbr = true; + } + + auto currentPass = renderPasses->head; + while (currentPass != nullptr) { + if (currentPass->shader->shaderType == RE::BSShader::Type::Lighting) { + constexpr uint32_t LightingTechniqueStart = 0x4800002D; + auto lightingTechnique = currentPass->passEnum - LightingTechniqueStart; + auto lightingFlags = lightingTechnique & ~(~0u << 24); + auto lightingType = static_cast((lightingTechnique >> 24) & 0x3F); + lightingFlags &= ~0b111000u; + if (isPbr) { + lightingFlags |= static_cast(SIE::ShaderCache::LightingShaderFlags::TruePbr); + } + lightingTechnique = (static_cast(lightingType) << 24) | lightingFlags; + currentPass->passEnum = lightingTechnique + LightingTechniqueStart; + } + currentPass = currentPass->next; + } + + return renderPasses; + } + static inline REL::Relocation func; +}; + +struct BSLightingShader_SetupMaterial +{ + static void thunk(RE::BSLightingShader* shader, RE::BSLightingShaderMaterialBase const* material) + { + using enum SIE::ShaderCache::LightingShaderTechniques; + + auto lightingFlags = shader->currentRawTechnique & ~(~0u << 24); + auto lightingType = static_cast((shader->currentRawTechnique >> 24) & 0x3F); + if (!(lightingType == LODLand || lightingType == LODLandNoise) && (lightingFlags & static_cast(SIE::ShaderCache::LightingShaderFlags::TruePbr))) { + auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); + auto renderer = RE::BSGraphics::Renderer::GetSingleton(); + + RE::BSGraphics::Renderer::PrepareVSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + RE::BSGraphics::Renderer::PreparePSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + + if (lightingType == MTLand || lightingType == MTLandLODBlend) { + auto* pbrMaterial = static_cast(material); + + constexpr size_t NormalStartIndex = 7; + + if (pbrMaterial->diffuseTexture != nullptr) { + shadowState->SetPSTexture(0, pbrMaterial->diffuseTexture->rendererTexture); + } + if (pbrMaterial->normalTexture != nullptr) { + shadowState->SetPSTexture(NormalStartIndex, pbrMaterial->normalTexture->rendererTexture); + } + if (pbrMaterial->landscapeDisplacementTextures[0] != nullptr) { + extendedRendererState.SetPSTexture(0, pbrMaterial->landscapeDisplacementTextures[0]->rendererTexture); + } + if (pbrMaterial->landscapeRMAOSTextures[0] != nullptr) { + extendedRendererState.SetPSTexture(BSLightingShaderMaterialPBRLandscape::NumTiles, pbrMaterial->landscapeRMAOSTextures[0]->rendererTexture); + } + for (uint32_t textureIndex = 1; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles; ++textureIndex) { + if (pbrMaterial->landscapeBaseColorTextures[textureIndex - 1] != nullptr) { + shadowState->SetPSTexture(textureIndex, pbrMaterial->landscapeBaseColorTextures[textureIndex - 1]->rendererTexture); + } + if (pbrMaterial->landscapeNormalTextures[textureIndex - 1] != nullptr) { + shadowState->SetPSTexture(NormalStartIndex + textureIndex, pbrMaterial->landscapeNormalTextures[textureIndex - 1]->rendererTexture); + } + if (pbrMaterial->landscapeDisplacementTextures[textureIndex] != nullptr) { + extendedRendererState.SetPSTexture(textureIndex, pbrMaterial->landscapeDisplacementTextures[textureIndex]->rendererTexture); + } + if (pbrMaterial->landscapeRMAOSTextures[textureIndex] != nullptr) { + extendedRendererState.SetPSTexture(BSLightingShaderMaterialPBRLandscape::NumTiles + textureIndex, pbrMaterial->landscapeRMAOSTextures[textureIndex]->rendererTexture); + } + } + + shadowState->SetPSTextureAddressMode(0, RE::BSGraphics::TextureAddressMode::kWrapSWrapT); + shadowState->SetPSTextureFilterMode(0, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + if (pbrMaterial->terrainOverlayTexture != nullptr) { + shadowState->SetPSTexture(13, pbrMaterial->terrainOverlayTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(13, RE::BSGraphics::TextureAddressMode::kClampSClampT); + shadowState->SetPSTextureFilterMode(13, RE::BSGraphics::TextureFilterMode::kAnisotropic); + } + + if (pbrMaterial->terrainNoiseTexture != nullptr) { + shadowState->SetPSTexture(15, pbrMaterial->terrainNoiseTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(15, RE::BSGraphics::TextureAddressMode::kWrapSWrapT); + shadowState->SetPSTextureFilterMode(15, RE::BSGraphics::TextureFilterMode::kBilinear); + } + + { + uint32_t flags = 0; + for (uint32_t textureIndex = 0; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles; ++textureIndex) { + if (pbrMaterial->isPbr[textureIndex]) { + flags |= (1 << textureIndex); + if (pbrMaterial->landscapeDisplacementTextures[textureIndex] != nullptr && pbrMaterial->landscapeDisplacementTextures[textureIndex] != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack) { + flags |= (1 << (BSLightingShaderMaterialPBRLandscape::NumTiles + textureIndex)); + } + } + } + shadowState->SetPSConstant(flags, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 36); + } + + { + constexpr size_t PBRParamsStartIndex = 37; + + for (uint32_t textureIndex = 0; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles; ++textureIndex) { + std::array PBRParams; + PBRParams[0] = pbrMaterial->roughnessScales[textureIndex]; + PBRParams[1] = pbrMaterial->displacementScales[textureIndex]; + PBRParams[2] = pbrMaterial->specularLevels[textureIndex]; + shadowState->SetPSConstant(PBRParams, RE::BSGraphics::ConstantGroupLevel::PerMaterial, PBRParamsStartIndex + textureIndex); + } + } + + { + std::array lodTexParams; + lodTexParams[0] = pbrMaterial->terrainTexOffsetX; + lodTexParams[1] = pbrMaterial->terrainTexOffsetY; + lodTexParams[2] = 1.f; + lodTexParams[3] = pbrMaterial->terrainTexFade; + shadowState->SetPSConstant(lodTexParams, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 24); + } + } else if (lightingType == None || lightingType == TreeAnim) { + auto* pbrMaterial = static_cast(material); + if (pbrMaterial->diffuseRenderTargetSourceIndex != -1) { + shadowState->SetPSTexture(0, renderer->GetRuntimeData().renderTargets[pbrMaterial->diffuseRenderTargetSourceIndex]); + } else { + shadowState->SetPSTexture(0, pbrMaterial->diffuseTexture->rendererTexture); + } + shadowState->SetPSTextureAddressMode(0, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(0, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shadowState->SetPSTexture(1, pbrMaterial->normalTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(1, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(1, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shadowState->SetPSTexture(5, pbrMaterial->rmaosTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(5, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(5, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + stl::enumeration shaderFlags; + if (pbrMaterial->pbrFlags.any(PBRFlags::TwoLayer)) { + shaderFlags.set(PBRShaderFlags::TwoLayer); + if (pbrMaterial->pbrFlags.any(PBRFlags::InterlayerParallax)) { + shaderFlags.set(PBRShaderFlags::InterlayerParallax); + } + if (pbrMaterial->pbrFlags.any(PBRFlags::CoatNormal)) { + shaderFlags.set(PBRShaderFlags::CoatNormal); + } + if (pbrMaterial->pbrFlags.any(PBRFlags::ColoredCoat)) { + shaderFlags.set(PBRShaderFlags::ColoredCoat); + } + + std::array PBRParams2; + PBRParams2[0] = pbrMaterial->GetCoatColor().red; + PBRParams2[1] = pbrMaterial->GetCoatColor().green; + PBRParams2[2] = pbrMaterial->GetCoatColor().blue; + PBRParams2[3] = pbrMaterial->GetCoatStrength(); + shadowState->SetPSConstant(PBRParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 43); + + std::array PBRParams3; + PBRParams3[0] = pbrMaterial->GetCoatRoughness(); + PBRParams3[1] = pbrMaterial->GetCoatSpecularLevel(); + shadowState->SetPSConstant(PBRParams3, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 27); + } else if (pbrMaterial->pbrFlags.any(PBRFlags::HairMarschner)) { + shaderFlags.set(PBRShaderFlags::HairMarschner); + } else { + if (pbrMaterial->pbrFlags.any(PBRFlags::Subsurface)) { + shaderFlags.set(PBRShaderFlags::Subsurface); + + std::array PBRParams2; + PBRParams2[0] = pbrMaterial->GetSubsurfaceColor().red; + PBRParams2[1] = pbrMaterial->GetSubsurfaceColor().green; + PBRParams2[2] = pbrMaterial->GetSubsurfaceColor().blue; + PBRParams2[3] = pbrMaterial->GetSubsurfaceOpacity(); + shadowState->SetPSConstant(PBRParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 43); + } + if (pbrMaterial->pbrFlags.any(PBRFlags::Fuzz)) { + shaderFlags.set(PBRShaderFlags::Fuzz); + + std::array PBRParams3; + PBRParams3[0] = pbrMaterial->GetFuzzColor().red; + PBRParams3[1] = pbrMaterial->GetFuzzColor().green; + PBRParams3[2] = pbrMaterial->GetFuzzColor().blue; + PBRParams3[3] = pbrMaterial->GetFuzzWeight(); + shadowState->SetPSConstant(PBRParams3, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 27); + } + } + + { + std::array PBRProjectedUVParams1; + PBRProjectedUVParams1[0] = pbrMaterial->GetProjectedMaterialBaseColorScale()[0]; + PBRProjectedUVParams1[1] = pbrMaterial->GetProjectedMaterialBaseColorScale()[1]; + PBRProjectedUVParams1[2] = pbrMaterial->GetProjectedMaterialBaseColorScale()[2]; + shadowState->SetPSConstant(PBRProjectedUVParams1, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 21); + + std::array PBRProjectedUVParams2; + PBRProjectedUVParams2[0] = pbrMaterial->GetProjectedMaterialRoughness(); + PBRProjectedUVParams2[1] = pbrMaterial->GetProjectedMaterialSpecularLevel(); + shadowState->SetPSConstant(PBRProjectedUVParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 22); + } + + const bool hasEmissive = pbrMaterial->emissiveTexture != nullptr && pbrMaterial->emissiveTexture != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack; + if (hasEmissive) { + shadowState->SetPSTexture(6, pbrMaterial->emissiveTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(6, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(6, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shaderFlags.set(PBRShaderFlags::HasEmissive); + } + + const bool hasDisplacement = pbrMaterial->displacementTexture != nullptr && pbrMaterial->displacementTexture != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack; + if (hasDisplacement) { + shadowState->SetPSTexture(4, pbrMaterial->displacementTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(4, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(4, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shaderFlags.set(PBRShaderFlags::HasDisplacement); + } + + const bool hasFeaturesTexture0 = pbrMaterial->featuresTexture0 != nullptr && pbrMaterial->featuresTexture0 != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureWhite; + if (hasFeaturesTexture0) { + shadowState->SetPSTexture(12, pbrMaterial->featuresTexture0->rendererTexture); + shadowState->SetPSTextureAddressMode(12, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(12, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shaderFlags.set(PBRShaderFlags::HasFeaturesTexture0); + } + + const bool hasFeaturesTexture1 = pbrMaterial->featuresTexture1 != nullptr && pbrMaterial->featuresTexture1 != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureWhite; + if (hasFeaturesTexture1) { + shadowState->SetPSTexture(9, pbrMaterial->featuresTexture1->rendererTexture); + shadowState->SetPSTextureAddressMode(9, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(9, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shaderFlags.set(PBRShaderFlags::HasFeaturesTexture1); + } + + { + shadowState->SetPSConstant(shaderFlags, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 36); + } + + { + std::array PBRParams1; + PBRParams1[0] = pbrMaterial->GetRoughnessScale(); + PBRParams1[1] = pbrMaterial->GetDisplacementScale(); + PBRParams1[2] = pbrMaterial->GetSpecularLevel(); + shadowState->SetPSConstant(PBRParams1, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 37); + } + } + + { + const uint32_t bufferIndex = RE::BSShaderManager::State::GetSingleton().textureTransformCurrentBuffer; + + std::array texCoordOffsetScale; + texCoordOffsetScale[0] = material->texCoordOffset[bufferIndex].x; + texCoordOffsetScale[1] = material->texCoordOffset[bufferIndex].y; + texCoordOffsetScale[2] = material->texCoordScale[bufferIndex].x; + texCoordOffsetScale[3] = material->texCoordScale[bufferIndex].y; + shadowState->SetVSConstant(texCoordOffsetScale, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 11); + } + + if (lightingFlags & static_cast(SIE::ShaderCache::LightingShaderFlags::CharacterLight)) { + static const REL::Relocation characterLightTexture{ RELOCATION_ID(513464, 391302) }; + + if (characterLightTexture->renderTarget >= RE::RENDER_TARGET::kFRAMEBUFFER) { + shadowState->SetPSTexture(11, renderer->GetRuntimeData().renderTargets[characterLightTexture->renderTarget]); + shadowState->SetPSTextureAddressMode(11, RE::BSGraphics::TextureAddressMode::kClampSClampT); + } + + const auto& smState = RE::BSShaderManager::State::GetSingleton(); + std::array characterLightParams; + if (smState.characterLightEnabled) { + std::copy_n(smState.characterLightParams, 4, characterLightParams.data()); + } else { + std::fill_n(characterLightParams.data(), 4, 0.f); + } + shadowState->SetPSConstant(characterLightParams, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 35); + } + + RE::BSGraphics::Renderer::FlushVSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + RE::BSGraphics::Renderer::FlushPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + RE::BSGraphics::Renderer::ApplyVSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + RE::BSGraphics::Renderer::ApplyPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + } else { + func(shader, material); + } + }; + static inline REL::Relocation func; +}; + +struct BSLightingShader_SetupGeometry +{ + static void thunk(RE::BSLightingShader* shader, RE::BSRenderPass* pass, uint32_t renderFlags) + { + const uint32_t originalExtraFlags = shader->currentRawTechnique & 0b111000u; + + if ((shader->currentRawTechnique & static_cast(SIE::ShaderCache::LightingShaderFlags::TruePbr)) != 0) { + shader->currentRawTechnique |= static_cast(SIE::ShaderCache::LightingShaderFlags::AmbientSpecular); + } + + shader->currentRawTechnique &= ~0b111000u; + shader->currentRawTechnique |= ((pass->numLights - 1) << 3); + + func(shader, pass, renderFlags); + + shader->currentRawTechnique &= ~0b111000u; + shader->currentRawTechnique |= originalExtraFlags; + + if ((shader->currentRawTechnique & static_cast(SIE::ShaderCache::LightingShaderFlags::TruePbr)) != 0) { + shader->currentRawTechnique &= ~static_cast(SIE::ShaderCache::LightingShaderFlags::AmbientSpecular); + } + } + static inline REL::Relocation func; +}; + +uint32_t hk_BSLightingShader_GetPixelTechnique(uint32_t rawTechnique) +{ + uint32_t pixelTechnique = rawTechnique; + + pixelTechnique &= ~0b111000000u; + if ((pixelTechnique & static_cast(SIE::ShaderCache::LightingShaderFlags::ModelSpaceNormals)) == 0) { + pixelTechnique &= ~static_cast(SIE::ShaderCache::LightingShaderFlags::Skinned); + } + pixelTechnique |= static_cast(SIE::ShaderCache::LightingShaderFlags::VC); + + return pixelTechnique; +} + +void SetupLandscapeTexture(BSLightingShaderMaterialPBRLandscape& material, RE::TESLandTexture& landTexture, uint32_t textureIndex) +{ + if (textureIndex >= 6) { + return; + } + + auto textureSet = landTexture.textureSet; + if (textureSet == nullptr) { + return; + } + + auto* textureSetData = TruePBR::GetSingleton()->GetPBRTextureSetData(landTexture.textureSet); + const bool isPbr = textureSetData != nullptr; + + textureSet->SetTexture(BSLightingShaderMaterialPBRLandscape::BaseColorTexture, textureIndex == 0 ? material.diffuseTexture : material.landscapeBaseColorTextures[textureIndex - 1]); + textureSet->SetTexture(BSLightingShaderMaterialPBRLandscape::NormalTexture, textureIndex == 0 ? material.normalTexture : material.landscapeNormalTextures[textureIndex - 1]); + + if (isPbr) { + textureSet->SetTexture(BSLightingShaderMaterialPBRLandscape::RmaosTexture, material.landscapeRMAOSTextures[textureIndex]); + textureSet->SetTexture(BSLightingShaderMaterialPBRLandscape::DisplacementTexture, material.landscapeDisplacementTextures[textureIndex]); + material.displacementScales[textureIndex] = textureSetData->displacementScale; + material.roughnessScales[textureIndex] = textureSetData->roughnessScale; + material.specularLevels[textureIndex] = textureSetData->specularLevel; + } + material.isPbr[textureIndex] = isPbr; + + if (textureIndex == 0) { + if (material.diffuseTexture != nullptr) { + material.numLandscapeTextures = std::max(material.numLandscapeTextures, 1u); + } + } else { + if (material.landscapeBaseColorTextures[textureIndex] != nullptr) { + material.numLandscapeTextures = std::max(material.numLandscapeTextures, textureIndex + 2); + } + } +} + +bool hk_TESObjectLAND_SetupMaterial(RE::TESObjectLAND* land); +decltype(&hk_TESObjectLAND_SetupMaterial) ptr_TESObjectLAND_SetupMaterial; + +bool hk_TESObjectLAND_SetupMaterial(RE::TESObjectLAND* land) +{ + auto* singleton = TruePBR::GetSingleton(); + + bool isPbr = false; + if (land->loadedData != nullptr) { + for (uint32_t quadIndex = 0; quadIndex < 4; ++quadIndex) { + if (land->loadedData->defQuadTextures[quadIndex] != nullptr) { + if (singleton->IsPBRTextureSet(land->loadedData->defQuadTextures[quadIndex]->textureSet)) { + isPbr = true; + break; + } + } + for (uint32_t textureIndex = 0; textureIndex < 6; ++textureIndex) { + if (land->loadedData->quadTextures[quadIndex][textureIndex] != nullptr) { + if (singleton->IsPBRTextureSet(land->loadedData->quadTextures[quadIndex][textureIndex]->textureSet)) { + isPbr = true; + break; + } + } + } + } + } + + if (!isPbr) { + return ptr_TESObjectLAND_SetupMaterial(land); + } + + static const auto settings = RE::INISettingCollection::GetSingleton(); + static const bool bEnableLandFade = settings->GetSetting("bEnableLandFade:Display"); + static const bool bDrawLandShadows = settings->GetSetting("bDrawLandShadows:Display"); + static const bool bLandSpecular = settings->GetSetting("bLandSpecular:Landscape"); + + if (land->loadedData != nullptr && land->loadedData->mesh[0] != nullptr) { + land->data.flags.set(static_cast(8)); + for (uint32_t quadIndex = 0; quadIndex < 4; ++quadIndex) { + auto shaderProperty = static_cast(RE::MemoryManager::GetSingleton()->Allocate(sizeof(RE::BSLightingShaderProperty), 0, false)); + shaderProperty->Ctor(); + + { + BSLightingShaderMaterialPBRLandscape srcMaterial; + shaderProperty->LinkMaterial(&srcMaterial, true); + } + + auto material = static_cast(shaderProperty->material); + const auto& stateData = RE::BSGraphics::State::GetSingleton()->GetRuntimeData(); + + material->diffuseTexture = stateData.defaultTextureBlack; + material->normalTexture = stateData.defaultTextureNormalMap; + material->landscapeDisplacementTextures[0] = stateData.defaultTextureBlack; + material->landscapeRMAOSTextures[0] = stateData.defaultTextureWhite; + for (uint32_t textureIndex = 0; textureIndex < BSLightingShaderMaterialPBRLandscape::NumTiles - 1; ++textureIndex) { + material->landscapeBaseColorTextures[textureIndex] = stateData.defaultTextureBlack; + material->landscapeNormalTextures[textureIndex] = stateData.defaultTextureNormalMap; + material->landscapeDisplacementTextures[textureIndex + 1] = stateData.defaultTextureBlack; + material->landscapeRMAOSTextures[textureIndex + 1] = stateData.defaultTextureWhite; + } + + if (auto defTexture = land->loadedData->defQuadTextures[quadIndex]) { + SetupLandscapeTexture(*material, *defTexture, 0); + } + for (uint32_t textureIndex = 0; textureIndex < 6; ++textureIndex) { + if (auto landTexture = land->loadedData->quadTextures[quadIndex][textureIndex]) { + SetupLandscapeTexture(*material, *landTexture, textureIndex + 1); + } + } + + if (bEnableLandFade) { + shaderProperty->unk108 = false; + } + + bool noLODLandBlend = false; + auto tes = RE::TES::GetSingleton(); + if (tes->worldSpace != nullptr) { + if (auto terrainManager = tes->worldSpace->GetTerrainManager()) { + noLODLandBlend = reinterpret_cast(terrainManager)[0x36]; + } + } + shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kMultiTextureLandscape, true); + shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kReceiveShadows, true); + shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kCastShadows, bDrawLandShadows); + shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kNoLODLandBlend, noLODLandBlend); + + shaderProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kVertexLighting, true); + + const auto& children = land->loadedData->mesh[quadIndex]->GetChildren(); + auto geometry = children.empty() ? nullptr : static_cast(children[0].get()); + shaderProperty->SetupGeometry(geometry); + if (geometry != nullptr) { + geometry->GetGeometryRuntimeData().properties[1] = RE::NiPointer(shaderProperty); + } + + RE::BSShaderManager::State::GetSingleton().shadowSceneNode[0]->AttachObject(geometry); + } + + return true; + } + + return false; +} + +struct TESForm_GetFormEditorID +{ + static const char* thunk(const RE::TESForm* form) + { + auto* singleton = TruePBR::GetSingleton(); + auto it = singleton->editorIDs.find(form->GetFormID()); + if (it == singleton->editorIDs.cend()) { + return ""; + } + return it->second.c_str(); + } + static inline REL::Relocation func; +}; + +struct TESForm_SetFormEditorID +{ + static bool thunk(RE::TESForm* form, const char* editorId) + { + auto* singleton = TruePBR::GetSingleton(); + singleton->editorIDs[form->GetFormID()] = editorId; + return true; + } + static inline REL::Relocation func; +}; + +void hk_SetPerFrameBuffers(void* renderer); +decltype(&hk_SetPerFrameBuffers) ptr_SetPerFrameBuffers; + +void hk_SetPerFrameBuffers(void* renderer) +{ + ptr_SetPerFrameBuffers(renderer); + TruePBR::GetSingleton()->SetupFrame(); +} + +void hk_BSTempEffectSimpleDecal_SetupGeometry(RE::BSTempEffectSimpleDecal* decal, RE::BSGeometry* geometry, RE::BGSTextureSet* textureSet, bool blended); +decltype(&hk_BSTempEffectSimpleDecal_SetupGeometry) ptr_BSTempEffectSimpleDecal_SetupGeometry; + +void hk_BSTempEffectSimpleDecal_SetupGeometry(RE::BSTempEffectSimpleDecal* decal, RE::BSGeometry* geometry, RE::BGSTextureSet* textureSet, bool blended) +{ + ptr_BSTempEffectSimpleDecal_SetupGeometry(decal, geometry, textureSet, blended); + + if (auto* shaderProperty = netimmerse_cast(geometry->GetGeometryRuntimeData().properties[1].get()); + shaderProperty != nullptr && TruePBR::GetSingleton()->IsPBRTextureSet(textureSet)) { + { + BSLightingShaderMaterialPBR srcMaterial; + shaderProperty->LinkMaterial(&srcMaterial, true); + } + + auto pbrMaterial = static_cast(shaderProperty->material); + pbrMaterial->OnLoadTextureSet(0, textureSet); + + constexpr static RE::NiColor whiteColor(1.f, 1.f, 1.f); + *shaderProperty->emissiveColor = whiteColor; + const bool hasEmissive = pbrMaterial->emissiveTexture != nullptr && pbrMaterial->emissiveTexture != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack; + shaderProperty->emissiveMult = hasEmissive ? 1.f : 0.f; + + { + using enum RE::BSShaderProperty::EShaderPropertyFlag8; + shaderProperty->SetFlags(kParallaxOcclusion, false); + shaderProperty->SetFlags(kParallax, false); + shaderProperty->SetFlags(kGlowMap, false); + shaderProperty->SetFlags(kEnvMap, false); + shaderProperty->SetFlags(kSpecular, false); + + shaderProperty->SetFlags(kVertexLighting, true); + } + } +} + +struct BSTempEffectGeometryDecal_Initialize +{ + static void thunk(RE::BSTempEffectGeometryDecal* decal) + { + func(decal); + + if (decal->decal != nullptr && TruePBR::GetSingleton()->IsPBRTextureSet(decal->texSet)) { + auto shaderProperty = static_cast(RE::MemoryManager::GetSingleton()->Allocate(sizeof(RE::BSLightingShaderProperty), 0, false)); + shaderProperty->Ctor(); + + { + BSLightingShaderMaterialPBR srcMaterial; + shaderProperty->LinkMaterial(&srcMaterial, true); + } + + auto pbrMaterial = static_cast(shaderProperty->material); + pbrMaterial->OnLoadTextureSet(0, decal->texSet); + + constexpr static RE::NiColor whiteColor(1.f, 1.f, 1.f); + *shaderProperty->emissiveColor = whiteColor; + const bool hasEmissive = pbrMaterial->emissiveTexture != nullptr && pbrMaterial->emissiveTexture != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureBlack; + shaderProperty->emissiveMult = hasEmissive ? 1.f : 0.f; + + { + using enum RE::BSShaderProperty::EShaderPropertyFlag8; + + shaderProperty->SetFlags(kSkinned, true); + shaderProperty->SetFlags(kDynamicDecal, true); + shaderProperty->SetFlags(kZBufferTest, true); + shaderProperty->SetFlags(kZBufferWrite, false); + + shaderProperty->SetFlags(kVertexLighting, true); + } + + if (auto* alphaProperty = static_cast(decal->decal->GetGeometryRuntimeData().properties[0].get())) { + alphaProperty->alphaFlags = (alphaProperty->alphaFlags & ~0x1FE) | 0xED; + } + + shaderProperty->SetupGeometry(decal->decal.get()); + decal->decal->GetGeometryRuntimeData().properties[1] = RE::NiPointer(shaderProperty); + } + } + static inline REL::Relocation func; +}; + +struct BSGrassShaderProperty_ctor +{ + static RE::BSLightingShaderProperty* thunk(RE::BSLightingShaderProperty* property) + { + const uint64_t stackPointer = reinterpret_cast(_AddressOfReturnAddress()); + const uint64_t lightingPropertyAddress = stackPointer + (REL::Module::IsAE() ? 0x68 : 0x70); + auto* lightingProperty = *reinterpret_cast(lightingPropertyAddress); + + RE::BSLightingShaderProperty* grassProperty = func(property); + + if (lightingProperty->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kVertexLighting)) { + if (auto* pbrSrcMaterial = static_cast(lightingProperty->material)) { + BSLightingShaderMaterialPBR srcMaterial; + grassProperty->LinkMaterial(&srcMaterial, true); + + grassProperty->SetFlags(RE::BSShaderProperty::EShaderPropertyFlag8::kMenuScreen, true); + + auto pbrMaterial = static_cast(grassProperty->material); + pbrMaterial->pbrFlags = pbrSrcMaterial->pbrFlags; + pbrMaterial->normalTexture = pbrSrcMaterial->normalTexture; + pbrMaterial->rmaosTexture = pbrSrcMaterial->rmaosTexture; + pbrMaterial->featuresTexture0 = pbrSrcMaterial->featuresTexture0; + pbrMaterial->featuresTexture1 = pbrSrcMaterial->featuresTexture1; + pbrMaterial->specularColorScale = pbrSrcMaterial->specularColorScale; + pbrMaterial->specularPower = pbrSrcMaterial->specularPower; + pbrMaterial->specularColor = pbrSrcMaterial->specularColor; + pbrMaterial->subSurfaceLightRolloff = pbrSrcMaterial->subSurfaceLightRolloff; + pbrMaterial->coatRoughness = pbrSrcMaterial->coatRoughness; + } + } + + return grassProperty; + } + static inline REL::Relocation func; +}; + +struct BSGrassShaderProperty_GetRenderPasses +{ + static RE::BSShaderProperty::RenderPassArray* thunk(RE::BSLightingShaderProperty* property, RE::BSGeometry* geometry, std::uint32_t renderFlags, RE::BSShaderAccumulator* accumulator) + { + auto renderPasses = func(property, geometry, renderFlags, accumulator); + if (renderPasses == nullptr) { + return renderPasses; + } + + const bool isPbr = property->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kMenuScreen); + if (isPbr) { + auto currentPass = renderPasses->head; + while (currentPass != nullptr) { + if (currentPass->shader->shaderType == RE::BSShader::Type::Grass && currentPass->passEnum != 0x5C00005C) { + currentPass->passEnum = 0x5C000042; + } + currentPass = currentPass->next; + } + } + + return renderPasses; + } + static inline REL::Relocation func; +}; + +struct BSGrassShader_SetupTechnique +{ + static bool thunk(RE::BSShader* shader, uint32_t globalTechnique) + { + if (globalTechnique == 0x5C000042) { + auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); + auto* graphicsState = RE::BSGraphics::State::GetSingleton(); + auto* renderer = RE::BSGraphics::Renderer::GetSingleton(); + + const uint32_t localTechnique = static_cast(SIE::ShaderCache::GrassShaderTechniques::TruePbr); + uint32_t shaderDescriptor = localTechnique; + if (graphicsState->useEarlyZ) { + shaderDescriptor |= static_cast(SIE::ShaderCache::GrassShaderFlags::AlphaTest); + } + + const bool began = Hooks::hk_BSShader_BeginTechnique(shader, shaderDescriptor, shaderDescriptor, false); + if (!began) { + return false; + } + + static auto fogMethod = REL::Relocation(REL::RelocationID(100000, 106707)); + fogMethod(); + + static auto* bShadowsOnGrass = RE::GetINISetting("bShadowsOnGrass:Display"); + if (!bShadowsOnGrass->GetBool()) { + shadowState->SetPSTexture(1, graphicsState->defaultTextureWhite->rendererTexture); + shadowState->SetPSTextureAddressMode(1, RE::BSGraphics::TextureAddressMode::kClampSClampT); + shadowState->SetPSTextureFilterMode(1, RE::BSGraphics::TextureFilterMode::kNearest); + } else { + shadowState->SetPSTexture(1, renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGET::kSHADOW_MASK]); + shadowState->SetPSTextureAddressMode(1, RE::BSGraphics::TextureAddressMode::kClampSClampT); + + static auto* shadowMaskQuarter = RE::GetINISetting("iShadowMaskQuarter:Display"); + shadowState->SetPSTextureFilterMode(1, shadowMaskQuarter->GetSInt() != 4 ? RE::BSGraphics::TextureFilterMode::kBilinear : RE::BSGraphics::TextureFilterMode::kNearest); + } + + return true; + } + + return func(shader, globalTechnique); + }; + static inline REL::Relocation func; +}; + +struct BSGrassShader_SetupMaterial +{ + static void thunk(RE::BSShader* shader, RE::BSLightingShaderMaterialBase const* material) + { + const auto& state = State::GetSingleton(); + const auto technique = static_cast(state->currentPixelDescriptor & 0b1111); + + if (technique == SIE::ShaderCache::GrassShaderTechniques::TruePbr) { + auto shadowState = RE::BSGraphics::RendererShadowState::GetSingleton(); + + RE::BSGraphics::Renderer::PreparePSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + + auto* pbrMaterial = static_cast(material); + shadowState->SetPSTexture(0, pbrMaterial->diffuseTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(0, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(0, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shadowState->SetPSTexture(2, pbrMaterial->normalTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(2, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(2, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shadowState->SetPSTexture(3, pbrMaterial->rmaosTexture->rendererTexture); + shadowState->SetPSTextureAddressMode(3, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(3, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + stl::enumeration shaderFlags; + if (pbrMaterial->pbrFlags.any(PBRFlags::Subsurface)) { + shaderFlags.set(PBRShaderFlags::Subsurface); + } + + const bool hasSubsurface = pbrMaterial->featuresTexture0 != nullptr && pbrMaterial->featuresTexture0 != RE::BSGraphics::State::GetSingleton()->GetRuntimeData().defaultTextureWhite; + if (hasSubsurface) { + shadowState->SetPSTexture(4, pbrMaterial->featuresTexture0->rendererTexture); + shadowState->SetPSTextureAddressMode(4, static_cast(pbrMaterial->textureClampMode)); + shadowState->SetPSTextureFilterMode(4, RE::BSGraphics::TextureFilterMode::kAnisotropic); + + shaderFlags.set(PBRShaderFlags::HasFeaturesTexture0); + } + + { + shadowState->SetPSConstant(shaderFlags, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 0); + } + + { + std::array PBRParams1; + PBRParams1[0] = pbrMaterial->GetRoughnessScale(); + PBRParams1[1] = pbrMaterial->GetSpecularLevel(); + shadowState->SetPSConstant(PBRParams1, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 1); + } + + { + std::array PBRParams2; + PBRParams2[0] = pbrMaterial->GetSubsurfaceColor().red; + PBRParams2[1] = pbrMaterial->GetSubsurfaceColor().green; + PBRParams2[2] = pbrMaterial->GetSubsurfaceColor().blue; + PBRParams2[3] = pbrMaterial->GetSubsurfaceOpacity(); + shadowState->SetPSConstant(PBRParams2, RE::BSGraphics::ConstantGroupLevel::PerMaterial, 2); + } + + RE::BSGraphics::Renderer::FlushPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + RE::BSGraphics::Renderer::ApplyPSConstantGroup(RE::BSGraphics::ConstantGroupLevel::PerMaterial); + } else { + func(shader, material); + } + }; + static inline REL::Relocation func; +}; + +struct TESBoundObject_Clone3D +{ + static RE::NiAVObject* thunk(RE::TESBoundObject* object, RE::TESObjectREFR* ref, bool arg3) + { + auto* result = func(object, ref, arg3); + if (result != nullptr && ref != nullptr && ref->data.objectReference != nullptr && ref->data.objectReference->formType == RE::FormType::Static) { + auto* stat = static_cast(ref->data.objectReference); + if (stat->data.materialObj != nullptr && stat->data.materialObj->directionalData.singlePass) { + if (auto* pbrData = TruePBR::GetSingleton()->GetPBRMaterialObjectData(stat->data.materialObj)) { + RE::BSVisit::TraverseScenegraphGeometries(result, [pbrData](RE::BSGeometry* geometry) { + if (auto* shaderProperty = static_cast(geometry->GetGeometryRuntimeData().properties[1].get())) { + if (shaderProperty->GetMaterialType() == RE::BSShaderMaterial::Type::kLighting && + shaderProperty->flags.any(RE::BSShaderProperty::EShaderPropertyFlag::kVertexLighting)) { + if (auto* material = static_cast(shaderProperty->material)) { + material->projectedMaterialBaseColorScale = pbrData->baseColorScale; + material->projectedMaterialRoughness = pbrData->roughness; + material->projectedMaterialSpecularLevel = pbrData->specularLevel; + } + } + } + + return RE::BSVisit::BSVisitControl::kContinue; + }); + } + } + } + return result; + } + static inline REL::Relocation func; +}; + +void TruePBR::PostPostLoad() +{ + logger::info("Hooking BSLightingShaderProperty"); + stl::write_vfunc<0x18, BSLightingShaderProperty_LoadBinary>(RE::VTABLE_BSLightingShaderProperty[0]); + stl::write_vfunc<0x2A, BSLightingShaderProperty_GetRenderPasses>(RE::VTABLE_BSLightingShaderProperty[0]); + + logger::info("Hooking BSLightingShader"); + stl::write_vfunc<0x4, BSLightingShader_SetupMaterial>(RE::VTABLE_BSLightingShader[0]); + stl::write_vfunc<0x6, BSLightingShader_SetupGeometry>(RE::VTABLE_BSLightingShader[0]); + std::ignore = Detours::X64::DetourFunction(REL::RelocationID(101633, 108700).address(), (uintptr_t)&hk_BSLightingShader_GetPixelTechnique); + + logger::info("Hooking TESObjectLAND"); + *(uintptr_t*)&ptr_TESObjectLAND_SetupMaterial = Detours::X64::DetourFunction(REL::RelocationID(18368, 18791).address(), (uintptr_t)&hk_TESObjectLAND_SetupMaterial); + + logger::info("Hooking TESLandTexture"); + stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_TESLandTexture[0]); + stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_TESLandTexture[0]); + stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_BGSTextureSet[0]); + stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_BGSTextureSet[0]); + stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_BGSMaterialObject[0]); + stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_BGSMaterialObject[0]); + stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_BGSLightingTemplate[0]); + stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_BGSLightingTemplate[0]); + stl::write_vfunc<0x32, TESForm_GetFormEditorID>(RE::VTABLE_TESWeather[0]); + stl::write_vfunc<0x33, TESForm_SetFormEditorID>(RE::VTABLE_TESWeather[0]); + + logger::info("Hooking SetPerFrameBuffers"); + *(uintptr_t*)&ptr_SetPerFrameBuffers = Detours::X64::DetourFunction(REL::RelocationID(75570, 77371).address(), (uintptr_t)&hk_SetPerFrameBuffers); + + logger::info("Hooking BSTempEffectSimpleDecal"); + *(uintptr_t*)&ptr_BSTempEffectSimpleDecal_SetupGeometry = Detours::X64::DetourFunction(REL::RelocationID(29253, 30108).address(), (uintptr_t)&hk_BSTempEffectSimpleDecal_SetupGeometry); + + logger::info("Hooking BSTempEffectGeometryDecal"); + stl::write_vfunc<0x25, BSTempEffectGeometryDecal_Initialize>(RE::VTABLE_BSTempEffectGeometryDecal[0]); + + logger::info("Hooking BSGrassShaderProperty::ctor"); + stl::write_thunk_call(REL::RelocationID(15214, 15383).address() + REL::Relocate(0x45B, 0x4F5)); + + logger::info("Hooking BSGrassShaderProperty"); + stl::write_vfunc<0x2A, BSGrassShaderProperty_GetRenderPasses>(RE::VTABLE_BSGrassShaderProperty[0]); + + logger::info("Hooking BSGrassShader"); + stl::write_vfunc<0x2, BSGrassShader_SetupTechnique>(RE::VTABLE_BSGrassShader[0]); + stl::write_vfunc<0x4, BSGrassShader_SetupMaterial>(RE::VTABLE_BSGrassShader[0]); + + logger::info("Hooking TESObjectSTAT"); + stl::write_vfunc<0x4A, TESBoundObject_Clone3D>(RE::VTABLE_TESObjectSTAT[0]); +} + +void TruePBR::SetShaderResouces() +{ + auto context = State::GetSingleton()->context; + for (uint32_t textureIndex = 0; textureIndex < ExtendedRendererState::NumPSTextures; ++textureIndex) { + if (extendedRendererState.PSResourceModifiedBits & (1 << textureIndex)) { + context->PSSetShaderResources(ExtendedRendererState::FirstPSTexture + textureIndex, 1, &extendedRendererState.PSTexture[textureIndex]); + } + } + extendedRendererState.PSResourceModifiedBits = 0; +} diff --git a/src/TruePBR.h b/src/TruePBR.h new file mode 100644 index 000000000..930bf82ba --- /dev/null +++ b/src/TruePBR.h @@ -0,0 +1,106 @@ +#pragma once + +struct TruePBR +{ +public: + static TruePBR* GetSingleton() + { + static TruePBR singleton; + return &singleton; + } + + inline std::string GetShortName() { return "TruePBR"; } + + void DrawSettings(); + void SetupResources(); + void LoadSettings(json& o_json); + void SaveSettings(json& o_json); + void PostPostLoad(); + + void SetShaderResouces(); + void GenerateShaderPermutations(RE::BSShader* shader); + + std::unordered_map editorIDs; + + float globalPBRDirectLightColorMultiplier = 1.f; + float globalPBRAmbientLightColorMultiplier = 1.f; + + float weatherPBRDirectionalLightColorMultiplier = 1.f; + float weatherPBRDirectionalAmbientLightColorMultiplier = 1.f; + + struct alignas(16) Settings + { + float directionalLightColorMultiplier = 1.f; + float pointLightColorMultiplier = 1.f; + float ambientLightColorMultiplier = 1.f; + uint32_t useMultipleScattering = true; + uint32_t useMultiBounceAO = true; + uint32_t pad[3]; + } settings{}; + + struct PBRTextureSetData + { + float roughnessScale = 1.f; + float displacementScale = 1.f; + float specularLevel = 0.04f; + + RE::NiColor subsurfaceColor; + float subsurfaceOpacity = 0.f; + + RE::NiColor coatColor = { 1.f, 1.f, 1.f }; + float coatStrength = 1.f; + float coatRoughness = 1.f; + float coatSpecularLevel = 0.04f; + float innerLayerDisplacementOffset = 0.f; + + RE::NiColor fuzzColor; + float fuzzWeight = 0.f; + }; + + void SetupFrame(); + + void SetupTextureSetData(); + PBRTextureSetData* GetPBRTextureSetData(const RE::TESForm* textureSet); + bool IsPBRTextureSet(const RE::TESForm* textureSet); + + std::unordered_map pbrTextureSets; + + struct PBRMaterialObjectData + { + std::array baseColorScale = { 1.f, 1.f, 1.f }; + float roughness = 1.f; + float specularLevel = 1.f; + }; + + void SetupMaterialObjectData(); + PBRMaterialObjectData* GetPBRMaterialObjectData(const RE::TESForm* materialObject); + bool IsPBRMaterialObject(const RE::TESForm* materialObject); + + std::unordered_map pbrMaterialObjects; + + struct PBRLightingTemplateData + { + float directionalLightColorScale = 1.f; + float directionalAmbientLightColorScale = 1.f; + }; + + void SetupLightingTemplateData(); + PBRLightingTemplateData* GetPBRLightingTemplateData(const RE::TESForm* lightingTemplate); + bool IsPBRLightingTemplate(const RE::TESForm* lightingTemplate); + void SavePBRLightingTemplateData(const std::string& editorId); + + std::unordered_map pbrLightingTemplates; + + struct PBRWeatherData + { + float directionalLightColorScale = 1.f; + float directionalAmbientLightColorScale = 1.f; + }; + + void SetupWeatherData(); + PBRWeatherData* GetPBRWeatherData(const RE::TESForm* weather); + bool IsPBRWeather(const RE::TESForm* weather); + void SavePBRWeatherData(const std::string& editorId); + + std::unordered_map pbrWeathers; +}; diff --git a/src/BSLightingShaderMaterialPBR.cpp b/src/TruePBR/BSLightingShaderMaterialPBR.cpp similarity index 98% rename from src/BSLightingShaderMaterialPBR.cpp rename to src/TruePBR/BSLightingShaderMaterialPBR.cpp index f7e12a8c2..bc469ee44 100644 --- a/src/BSLightingShaderMaterialPBR.cpp +++ b/src/TruePBR/BSLightingShaderMaterialPBR.cpp @@ -1,6 +1,6 @@ #include "BSLightingShaderMaterialPBR.h" -#include "State.h" +#include "TruePBR.h" BSLightingShaderMaterialPBR::~BSLightingShaderMaterialPBR() {} @@ -123,7 +123,7 @@ void BSLightingShaderMaterialPBR::OnLoadTextureSet(std::uint64_t arg1, RE::BSTex textureSet->SetTexture(FeaturesTexture1, featuresTexture1); if (auto* bgsTextureSet = netimmerse_cast(inTextureSet); bgsTextureSet != nullptr) { - if (auto* textureSetData = State::GetSingleton()->GetPBRTextureSetData(bgsTextureSet)) { + if (auto* textureSetData = TruePBR::GetSingleton()->GetPBRTextureSetData(bgsTextureSet)) { specularColorScale = textureSetData->roughnessScale; specularPower = textureSetData->specularLevel; rimLightPower = textureSetData->displacementScale; diff --git a/include/BSLightingShaderMaterialPBR.h b/src/TruePBR/BSLightingShaderMaterialPBR.h similarity index 99% rename from include/BSLightingShaderMaterialPBR.h rename to src/TruePBR/BSLightingShaderMaterialPBR.h index 8735afe85..bacc25a34 100644 --- a/include/BSLightingShaderMaterialPBR.h +++ b/src/TruePBR/BSLightingShaderMaterialPBR.h @@ -49,7 +49,7 @@ class BSLightingShaderMaterialPBR : public RE::BSLightingShaderMaterialBase void ReceiveValuesFromRootMaterial(bool skinned, bool rimLighting, bool softLighting, bool backLighting, bool MSN) override; // 0A uint32_t GetTextures(RE::NiSourceTexture** textures) override; // 0B void LoadBinary(RE::NiStream& stream) override; // 0D - + static BSLightingShaderMaterialPBR* Make(); float GetRoughnessScale() const; diff --git a/src/BSLightingShaderMaterialPBRLandscape.cpp b/src/TruePBR/BSLightingShaderMaterialPBRLandscape.cpp similarity index 100% rename from src/BSLightingShaderMaterialPBRLandscape.cpp rename to src/TruePBR/BSLightingShaderMaterialPBRLandscape.cpp diff --git a/include/BSLightingShaderMaterialPBRLandscape.h b/src/TruePBR/BSLightingShaderMaterialPBRLandscape.h similarity index 100% rename from include/BSLightingShaderMaterialPBRLandscape.h rename to src/TruePBR/BSLightingShaderMaterialPBRLandscape.h