diff --git a/features/Cloud Shadows/Shaders/Features/CloudShadows.ini b/features/Cloud Shadows/Shaders/Features/CloudShadows.ini index 7c4d5a2a34..a2a6fd9ffb 100644 --- a/features/Cloud Shadows/Shaders/Features/CloudShadows.ini +++ b/features/Cloud Shadows/Shaders/Features/CloudShadows.ini @@ -1,2 +1,4 @@ -[Info] -Version = 1-2-0 \ No newline at end of file +[Info] +Version = 1-2-0 +NexusFileGroupID = +NexusModID = 139185 \ No newline at end of file diff --git a/features/Dynamic Cubemaps/Shaders/Features/DynamicCubemaps.ini b/features/Dynamic Cubemaps/Shaders/Features/DynamicCubemaps.ini index 7e78c8c4b6..1c2fbb4738 100644 --- a/features/Dynamic Cubemaps/Shaders/Features/DynamicCubemaps.ini +++ b/features/Dynamic Cubemaps/Shaders/Features/DynamicCubemaps.ini @@ -1,2 +1,4 @@ [Info] -Version = 2-3-0 \ No newline at end of file +Version = 2-3-0 +NexusFileGroupID = +NexusModID = \ No newline at end of file diff --git a/features/Exponential Height Fog/Shaders/Features/ExponentialHeightFog.ini b/features/Exponential Height Fog/Shaders/Features/ExponentialHeightFog.ini index 19f01444dc..65053f08ac 100644 --- a/features/Exponential Height Fog/Shaders/Features/ExponentialHeightFog.ini +++ b/features/Exponential Height Fog/Shaders/Features/ExponentialHeightFog.ini @@ -1,2 +1,4 @@ -[Info] -Version = 1-0-0 \ No newline at end of file +[Info] +Version = 1-0-0 +NexusFileGroupID = +NexusModID = \ No newline at end of file diff --git a/features/Extended Materials/Shaders/Features/ExtendedMaterials.ini b/features/Extended Materials/Shaders/Features/ExtendedMaterials.ini index de2206be54..baf79c9879 100644 --- a/features/Extended Materials/Shaders/Features/ExtendedMaterials.ini +++ b/features/Extended Materials/Shaders/Features/ExtendedMaterials.ini @@ -1,2 +1,4 @@ -[Info] -Version = 1-1-1 \ No newline at end of file +[Info] +Version = 1-1-1 +NexusFileGroupID = +NexusModID = \ No newline at end of file diff --git a/features/Extended Translucency/Shaders/Features/ExtendedTranslucency.ini b/features/Extended Translucency/Shaders/Features/ExtendedTranslucency.ini index 000b60a568..04a999f01e 100644 --- a/features/Extended Translucency/Shaders/Features/ExtendedTranslucency.ini +++ b/features/Extended Translucency/Shaders/Features/ExtendedTranslucency.ini @@ -1,2 +1,4 @@ -[Info] -Version = 1-0-0 +[Info] +Version = 1-0-0 +NexusFileGroupID = 6156484 +NexusModID = 150755 diff --git a/features/Grass Collision/Shaders/Features/GrassCollision.ini b/features/Grass Collision/Shaders/Features/GrassCollision.ini index 0514cb92bf..ec81c743f7 100644 --- a/features/Grass Collision/Shaders/Features/GrassCollision.ini +++ b/features/Grass Collision/Shaders/Features/GrassCollision.ini @@ -1,2 +1,4 @@ [Info] -Version = 3-0-3 \ No newline at end of file +Version = 3-0-3 +NexusFileGroupID = +NexusModID = 87816 \ No newline at end of file diff --git a/features/Grass Lighting/Shaders/Features/GrassLighting.ini b/features/Grass Lighting/Shaders/Features/GrassLighting.ini index 38b022aa86..d168dacbc8 100644 --- a/features/Grass Lighting/Shaders/Features/GrassLighting.ini +++ b/features/Grass Lighting/Shaders/Features/GrassLighting.ini @@ -1,2 +1,4 @@ [Info] -Version = 2-0-1 \ No newline at end of file +Version = 2-0-1 +NexusFileGroupID = 3119146 +NexusModID = 86502 \ No newline at end of file diff --git a/features/Hair Specular/Shaders/Features/HairSpecular.ini b/features/Hair Specular/Shaders/Features/HairSpecular.ini index 38f9201738..8c3da13b23 100644 --- a/features/Hair Specular/Shaders/Features/HairSpecular.ini +++ b/features/Hair Specular/Shaders/Features/HairSpecular.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-1-0 \ No newline at end of file +Version = 1-1-0 +NexusFileGroupID = +NexusModID = 149011 \ No newline at end of file diff --git a/features/IBL/Shaders/Features/ImageBasedLighting.ini b/features/IBL/Shaders/Features/ImageBasedLighting.ini index 38f9201738..f1429513f3 100644 --- a/features/IBL/Shaders/Features/ImageBasedLighting.ini +++ b/features/IBL/Shaders/Features/ImageBasedLighting.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-1-0 \ No newline at end of file +Version = 1-1-0 +NexusFileGroupID = +NexusModID = \ No newline at end of file diff --git a/features/Interior Sun/Shaders/Features/InteriorSun.ini b/features/Interior Sun/Shaders/Features/InteriorSun.ini index 19f01444dc..343ef90e3c 100644 --- a/features/Interior Sun/Shaders/Features/InteriorSun.ini +++ b/features/Interior Sun/Shaders/Features/InteriorSun.ini @@ -1,2 +1,4 @@ -[Info] -Version = 1-0-0 \ No newline at end of file +[Info] +Version = 1-0-0 +NexusFileGroupID = +NexusModID = 153541 \ No newline at end of file diff --git a/features/Inverse Square Lighting/Shaders/Features/InverseSquareLighting.ini b/features/Inverse Square Lighting/Shaders/Features/InverseSquareLighting.ini index 7c4d5a2a34..3429a87301 100644 --- a/features/Inverse Square Lighting/Shaders/Features/InverseSquareLighting.ini +++ b/features/Inverse Square Lighting/Shaders/Features/InverseSquareLighting.ini @@ -1,2 +1,4 @@ -[Info] -Version = 1-2-0 \ No newline at end of file +[Info] +Version = 1-2-0 +NexusFileGroupID = +NexusModID = 153542 \ No newline at end of file diff --git a/features/LOD Blending/Shaders/Features/LODBlending.ini b/features/LOD Blending/Shaders/Features/LODBlending.ini index 19f01444dc..65053f08ac 100644 --- a/features/LOD Blending/Shaders/Features/LODBlending.ini +++ b/features/LOD Blending/Shaders/Features/LODBlending.ini @@ -1,2 +1,4 @@ -[Info] -Version = 1-0-0 \ No newline at end of file +[Info] +Version = 1-0-0 +NexusFileGroupID = +NexusModID = \ No newline at end of file diff --git a/features/Light Limit Fix/Shaders/Features/LightLimitFix.ini b/features/Light Limit Fix/Shaders/Features/LightLimitFix.ini index d38552c323..17a2950e70 100644 --- a/features/Light Limit Fix/Shaders/Features/LightLimitFix.ini +++ b/features/Light Limit Fix/Shaders/Features/LightLimitFix.ini @@ -1,2 +1,4 @@ -[Info] -Version = 3-0-2 +[Info] +Version = 3-0-2 +NexusFileGroupID = 3150964 +NexusModID = 99548 diff --git a/features/Linear Lighting/Shaders/Features/LinearLighting.ini b/features/Linear Lighting/Shaders/Features/LinearLighting.ini index 38f9201738..f1429513f3 100644 --- a/features/Linear Lighting/Shaders/Features/LinearLighting.ini +++ b/features/Linear Lighting/Shaders/Features/LinearLighting.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-1-0 \ No newline at end of file +Version = 1-1-0 +NexusFileGroupID = +NexusModID = \ No newline at end of file diff --git a/features/Performance Overlay/Shaders/Features/PerformanceOverlay.ini b/features/Performance Overlay/Shaders/Features/PerformanceOverlay.ini index 735cfd23a9..920c1380c9 100644 --- a/features/Performance Overlay/Shaders/Features/PerformanceOverlay.ini +++ b/features/Performance Overlay/Shaders/Features/PerformanceOverlay.ini @@ -1,2 +1,4 @@ -[Info] -Version = 1-0-1 \ No newline at end of file +[Info] +Version = 1-0-1 +NexusFileGroupID = +NexusModID = \ No newline at end of file diff --git a/features/RenderDoc/Shaders/Features/RenderDoc.ini b/features/RenderDoc/Shaders/Features/RenderDoc.ini index 9bb09287c3..65053f08ac 100644 --- a/features/RenderDoc/Shaders/Features/RenderDoc.ini +++ b/features/RenderDoc/Shaders/Features/RenderDoc.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-0-0 \ No newline at end of file +Version = 1-0-0 +NexusFileGroupID = +NexusModID = \ No newline at end of file diff --git a/features/Screen Space GI/Shaders/Features/ScreenSpaceGI.ini b/features/Screen Space GI/Shaders/Features/ScreenSpaceGI.ini index b47cc502a6..5d32cac533 100644 --- a/features/Screen Space GI/Shaders/Features/ScreenSpaceGI.ini +++ b/features/Screen Space GI/Shaders/Features/ScreenSpaceGI.ini @@ -1,2 +1,4 @@ [Info] -Version = 4-1-0 \ No newline at end of file +Version = 4-1-0 +NexusFileGroupID = +NexusModID = 130375 \ No newline at end of file diff --git a/features/Screen-Space Shadows/Shaders/Features/ScreenSpaceShadows.ini b/features/Screen-Space Shadows/Shaders/Features/ScreenSpaceShadows.ini index 76c57cb24f..71289b262f 100644 --- a/features/Screen-Space Shadows/Shaders/Features/ScreenSpaceShadows.ini +++ b/features/Screen-Space Shadows/Shaders/Features/ScreenSpaceShadows.ini @@ -1,2 +1,4 @@ [Info] -Version = 2-1-0 \ No newline at end of file +Version = 2-1-0 +NexusFileGroupID = 3138904 +NexusModID = 93209 \ No newline at end of file diff --git a/features/Sky Sync/Shaders/Features/SkySync.ini b/features/Sky Sync/Shaders/Features/SkySync.ini index 3039b0b786..ac75880484 100644 --- a/features/Sky Sync/Shaders/Features/SkySync.ini +++ b/features/Sky Sync/Shaders/Features/SkySync.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-0-1 \ No newline at end of file +Version = 1-0-1 +NexusFileGroupID = +NexusModID = 153543 \ No newline at end of file diff --git a/features/Skylighting/Shaders/Features/Skylighting.ini b/features/Skylighting/Shaders/Features/Skylighting.ini index 82b4686c15..f48410b6ba 100644 --- a/features/Skylighting/Shaders/Features/Skylighting.ini +++ b/features/Skylighting/Shaders/Features/Skylighting.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-2-4 \ No newline at end of file +Version = 1-2-4 +NexusFileGroupID = +NexusModID = 139352 \ No newline at end of file diff --git a/features/Subsurface Scattering/Shaders/Features/SubsurfaceScattering.ini b/features/Subsurface Scattering/Shaders/Features/SubsurfaceScattering.ini index b889ce2cc9..18962397a5 100644 --- a/features/Subsurface Scattering/Shaders/Features/SubsurfaceScattering.ini +++ b/features/Subsurface Scattering/Shaders/Features/SubsurfaceScattering.ini @@ -1,2 +1,4 @@ -[Info] -Version = 3-0-1 \ No newline at end of file +[Info] +Version = 3-0-1 +NexusFileGroupID = +NexusModID = 114114 \ No newline at end of file diff --git a/features/Terrain Blending/Shaders/Features/TerrainBlending.ini b/features/Terrain Blending/Shaders/Features/TerrainBlending.ini index 5a3741e7c9..92a2ad3e26 100644 --- a/features/Terrain Blending/Shaders/Features/TerrainBlending.ini +++ b/features/Terrain Blending/Shaders/Features/TerrainBlending.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-0-2 \ No newline at end of file +Version = 1-0-2 +NexusFileGroupID = +NexusModID = 157076 \ No newline at end of file diff --git a/features/Terrain Helper/Shaders/Features/TerrainHelper.ini b/features/Terrain Helper/Shaders/Features/TerrainHelper.ini index 19f01444dc..40c6b3b281 100644 --- a/features/Terrain Helper/Shaders/Features/TerrainHelper.ini +++ b/features/Terrain Helper/Shaders/Features/TerrainHelper.ini @@ -1,2 +1,4 @@ -[Info] -Version = 1-0-0 \ No newline at end of file +[Info] +Version = 1-0-0 +NexusFileGroupID = +NexusModID = 143149 \ No newline at end of file diff --git a/features/Terrain Shadows/Shaders/Features/TerrainShadows.ini b/features/Terrain Shadows/Shaders/Features/TerrainShadows.ini index 38f9201738..426c98cb86 100644 --- a/features/Terrain Shadows/Shaders/Features/TerrainShadows.ini +++ b/features/Terrain Shadows/Shaders/Features/TerrainShadows.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-1-0 \ No newline at end of file +Version = 1-1-0 +NexusFileGroupID = +NexusModID = 135817 \ No newline at end of file diff --git a/features/Terrain Variation/Shaders/Features/TerrainVariation.ini b/features/Terrain Variation/Shaders/Features/TerrainVariation.ini index 735cfd23a9..b2450b3128 100644 --- a/features/Terrain Variation/Shaders/Features/TerrainVariation.ini +++ b/features/Terrain Variation/Shaders/Features/TerrainVariation.ini @@ -1,2 +1,4 @@ -[Info] -Version = 1-0-1 \ No newline at end of file +[Info] +Version = 1-0-1 +NexusFileGroupID = +NexusModID = 148123 \ No newline at end of file diff --git a/features/Unified Water/Shaders/Features/UnifiedWater.ini b/features/Unified Water/Shaders/Features/UnifiedWater.ini index 3039b0b786..920c1380c9 100644 --- a/features/Unified Water/Shaders/Features/UnifiedWater.ini +++ b/features/Unified Water/Shaders/Features/UnifiedWater.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-0-1 \ No newline at end of file +Version = 1-0-1 +NexusFileGroupID = +NexusModID = \ No newline at end of file diff --git a/features/Upscaling/Shaders/Features/Upscaling.ini b/features/Upscaling/Shaders/Features/Upscaling.ini index 8dc1735318..7f99fdede9 100644 --- a/features/Upscaling/Shaders/Features/Upscaling.ini +++ b/features/Upscaling/Shaders/Features/Upscaling.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-3-0 \ No newline at end of file +Version = 1-3-0 +NexusFileGroupID = +NexusModID = 156952 \ No newline at end of file diff --git a/features/VR/Shaders/Features/VR.ini b/features/VR/Shaders/Features/VR.ini index 38f9201738..f1429513f3 100644 --- a/features/VR/Shaders/Features/VR.ini +++ b/features/VR/Shaders/Features/VR.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-1-0 \ No newline at end of file +Version = 1-1-0 +NexusFileGroupID = +NexusModID = \ No newline at end of file diff --git a/features/Volumetric Lighting/Shaders/Features/VolumetricLighting.ini b/features/Volumetric Lighting/Shaders/Features/VolumetricLighting.ini index 38f9201738..f1429513f3 100644 --- a/features/Volumetric Lighting/Shaders/Features/VolumetricLighting.ini +++ b/features/Volumetric Lighting/Shaders/Features/VolumetricLighting.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-1-0 \ No newline at end of file +Version = 1-1-0 +NexusFileGroupID = +NexusModID = \ No newline at end of file diff --git a/features/Volumetric Shadows/Shaders/Features/VolumetricShadows.ini b/features/Volumetric Shadows/Shaders/Features/VolumetricShadows.ini index 65dc687acd..34caf7e365 100644 --- a/features/Volumetric Shadows/Shaders/Features/VolumetricShadows.ini +++ b/features/Volumetric Shadows/Shaders/Features/VolumetricShadows.ini @@ -1,2 +1,4 @@ -[Info] -Version = 2-0-0 +[Info] +Version = 2-0-0 +NexusFileGroupID = +NexusModID = diff --git a/features/Water Effects/Shaders/Features/WaterEffects.ini b/features/Water Effects/Shaders/Features/WaterEffects.ini index 312d7ff985..aed12e71e3 100644 --- a/features/Water Effects/Shaders/Features/WaterEffects.ini +++ b/features/Water Effects/Shaders/Features/WaterEffects.ini @@ -1,2 +1,4 @@ -[Info] -Version = 1-1-0 \ No newline at end of file +[Info] +Version = 1-1-0 +NexusFileGroupID = +NexusModID = 112762 \ No newline at end of file diff --git a/features/Weather Editor/Shaders/Features/WeatherEditor.ini b/features/Weather Editor/Shaders/Features/WeatherEditor.ini index 38f9201738..f1429513f3 100644 --- a/features/Weather Editor/Shaders/Features/WeatherEditor.ini +++ b/features/Weather Editor/Shaders/Features/WeatherEditor.ini @@ -1,2 +1,4 @@ [Info] -Version = 1-1-0 \ No newline at end of file +Version = 1-1-0 +NexusFileGroupID = +NexusModID = \ No newline at end of file diff --git a/features/Wetness Effects/Shaders/Features/WetnessEffects.ini b/features/Wetness Effects/Shaders/Features/WetnessEffects.ini index 67e8ddf6d2..4526259dda 100644 --- a/features/Wetness Effects/Shaders/Features/WetnessEffects.ini +++ b/features/Wetness Effects/Shaders/Features/WetnessEffects.ini @@ -1,2 +1,4 @@ [Info] -Version = 3-1-0 \ No newline at end of file +Version = 3-1-0 +NexusFileGroupID = 3176515 +NexusModID = 112739 \ No newline at end of file diff --git a/src/Feature.cpp b/src/Feature.cpp index 24b634979f..0b51e5a0b7 100644 --- a/src/Feature.cpp +++ b/src/Feature.cpp @@ -62,6 +62,12 @@ void Feature::Load(json& o_json) return; } + // Read Nexus metadata (populated regardless of version compatibility) + if (auto nexusId = ini.GetValue("Info", "NexusModID"); nexusId && *nexusId) + nexusModID = nexusId; + if (auto nexusFileGroup = ini.GetValue("Info", "NexusFileGroupID"); nexusFileGroup && *nexusFileGroup) + nexusFileGroupID = nexusFileGroup; + bool hasError = false; std::string errorVersion; FeatureIssues::FeatureIssueInfo::IssueType errorType = FeatureIssues::FeatureIssueInfo::IssueType::UNKNOWN; diff --git a/src/Feature.h b/src/Feature.h index f8992574bd..706cd0a196 100644 --- a/src/Feature.h +++ b/src/Feature.h @@ -24,11 +24,13 @@ struct Feature static constexpr std::string_view NEXUS_BASE_URL = "https://www.nexusmods.com/skyrimspecialedition/mods/"; bool loaded = false; std::string version; + std::string nexusModID; + std::string nexusFileGroupID; std::string failedLoadedMessage; virtual std::string GetName() = 0; virtual std::string GetShortName() = 0; - virtual std::string GetFeatureModLink() { return ""; } + virtual std::string GetFeatureModLink() { return nexusModID.empty() ? "" : MakeNexusModURL(nexusModID); } virtual std::string_view GetShaderDefineName() { return ""; } virtual std::vector> GetShaderDefineOptions() { return {}; } diff --git a/src/Features/CloudShadows.h b/src/Features/CloudShadows.h index 3ab14cbfef..5cd8f77c06 100644 --- a/src/Features/CloudShadows.h +++ b/src/Features/CloudShadows.h @@ -1,78 +1,75 @@ -#pragma once - -struct CloudShadows : Feature -{ -private: - static constexpr std::string_view MOD_ID = "139185"; - -public: - struct alignas(16) Settings - { - float Opacity = 0.8f; - float pad[3]; - }; - - Settings settings; - - virtual inline std::string GetName() override { return "Cloud Shadows"; } - virtual inline std::string GetShortName() override { return "CloudShadows"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual std::string_view GetCategory() const override { return FeatureCategories::kSky; } - virtual inline std::string_view GetShaderDefineName() override { return "CLOUD_SHADOWS"; } - virtual std::pair> GetFeatureSummary() override - { - return { - "Adds realistic cloud shadows that move across the landscape, creating dynamic lighting changes as clouds pass overhead, enhancing atmospheric immersion.", - { "Dynamic cloud shadow projection on terrain and objects", - "Configurable shadow opacity for artistic control", - "Real-time shadow movement synchronized with cloud motion", - "Cubemap-based shadow calculation for accurate projection", - "Enhanced sky rendering integration" } - }; - } - virtual inline bool HasShaderDefine(RE::BSShader::Type) override { return true; } - - bool overrideSky = false; - void SkyShaderHacks(); - - Texture2D* texCubemapCloudOcc = nullptr; - Texture2D* texCubemapCloudOccCopy = nullptr; - - ID3D11RenderTargetView* cubemapCloudOccRTVs[6] = { nullptr }; - ID3D11RenderTargetView* cubemapCloudOccCopyRTVs[6] = { nullptr }; - - ID3D11BlendState* cloudShadowBlendState = nullptr; - - virtual void SetupResources() override; - - virtual void DrawSettings() override; - - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - - virtual void RestoreDefaultSettings() override; - - void CheckResourcesSide(int side); - void ModifySky(RE::BSRenderPass* Pass); - - virtual void ReflectionsPrepass() override; - virtual void EarlyPrepass() override; - - virtual inline void PostPostLoad() override { Hooks::Install(); } - - struct Hooks - { - struct BSSkyShader_SetupMaterial - { - static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); - static inline REL::Relocation func; - }; - - static void Install() - { - stl::write_vfunc<0x6, BSSkyShader_SetupMaterial>(RE::VTABLE_BSSkyShader[0]); - logger::info("[Cloud Shadows] Installed hooks"); - } - }; - virtual bool SupportsVR() override { return true; }; -}; +#pragma once + +struct CloudShadows : Feature +{ +private: +public: + struct alignas(16) Settings + { + float Opacity = 0.8f; + float pad[3]; + }; + + Settings settings; + + virtual inline std::string GetName() override { return "Cloud Shadows"; } + virtual inline std::string GetShortName() override { return "CloudShadows"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kSky; } + virtual inline std::string_view GetShaderDefineName() override { return "CLOUD_SHADOWS"; } + virtual std::pair> GetFeatureSummary() override + { + return { + "Adds realistic cloud shadows that move across the landscape, creating dynamic lighting changes as clouds pass overhead, enhancing atmospheric immersion.", + { "Dynamic cloud shadow projection on terrain and objects", + "Configurable shadow opacity for artistic control", + "Real-time shadow movement synchronized with cloud motion", + "Cubemap-based shadow calculation for accurate projection", + "Enhanced sky rendering integration" } + }; + } + virtual inline bool HasShaderDefine(RE::BSShader::Type) override { return true; } + + bool overrideSky = false; + void SkyShaderHacks(); + + Texture2D* texCubemapCloudOcc = nullptr; + Texture2D* texCubemapCloudOccCopy = nullptr; + + ID3D11RenderTargetView* cubemapCloudOccRTVs[6] = { nullptr }; + ID3D11RenderTargetView* cubemapCloudOccCopyRTVs[6] = { nullptr }; + + ID3D11BlendState* cloudShadowBlendState = nullptr; + + virtual void SetupResources() override; + + virtual void DrawSettings() override; + + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + virtual void RestoreDefaultSettings() override; + + void CheckResourcesSide(int side); + void ModifySky(RE::BSRenderPass* Pass); + + virtual void ReflectionsPrepass() override; + virtual void EarlyPrepass() override; + + virtual inline void PostPostLoad() override { Hooks::Install(); } + + struct Hooks + { + struct BSSkyShader_SetupMaterial + { + static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); + static inline REL::Relocation func; + }; + + static void Install() + { + stl::write_vfunc<0x6, BSSkyShader_SetupMaterial>(RE::VTABLE_BSSkyShader[0]); + logger::info("[Cloud Shadows] Installed hooks"); + } + }; + virtual bool SupportsVR() override { return true; }; +}; diff --git a/src/Features/ExponentialHeightFog.h b/src/Features/ExponentialHeightFog.h index 0bfadd6b4a..f37c98fa9d 100644 --- a/src/Features/ExponentialHeightFog.h +++ b/src/Features/ExponentialHeightFog.h @@ -1,48 +1,47 @@ -#pragma once - -struct ExponentialHeightFog : Feature -{ - virtual bool SupportsVR() override { return true; }; - virtual inline std::string GetName() override { return "Exponential Height Fog"; } - virtual inline std::string GetShortName() override { return "ExponentialHeightFog"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL("999999"); } - virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } - - virtual inline std::pair> GetFeatureSummary() override - { - return { - "Exponential Height Fog adds a realistic fog effect that increases in density with height, enhancing atmospheric depth and immersion in the game environment.", - { - "Added exponential height fog effect", - "Adapted to vanilla fog settings", - "Creates atmospheric depth", - } - }; - } - - virtual inline std::string_view GetShaderDefineName() override { return "EXP_HEIGHT_FOG"; } - bool HasShaderDefine(RE::BSShader::Type) override { return true; }; - - virtual void DrawSettings() override; - - virtual void RestoreDefaultSettings() override; - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - - void RegisterWeatherVariables() override; - - struct alignas(16) Settings - { - uint enabled = 1; - uint useDynamicCubemaps = 0; - float startDistance = 0.0f; - float fogHeight = 0.0f; - float fogHeightFalloff = 0.2f; - float fogDensity = 0.02f; - float directionalInscatteringMultiplier = 1.0f; - float directionalInscatteringExponent = 4.0f; - float4 inscatteringTint = { 1.0f, 1.0f, 1.0f, 1.0f }; - float cubemapMipLevel = 3.0f; - float pad[3]; - } settings; +#pragma once + +struct ExponentialHeightFog : Feature +{ + virtual bool SupportsVR() override { return true; }; + virtual inline std::string GetName() override { return "Exponential Height Fog"; } + virtual inline std::string GetShortName() override { return "ExponentialHeightFog"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } + + virtual inline std::pair> GetFeatureSummary() override + { + return { + "Exponential Height Fog adds a realistic fog effect that increases in density with height, enhancing atmospheric depth and immersion in the game environment.", + { + "Added exponential height fog effect", + "Adapted to vanilla fog settings", + "Creates atmospheric depth", + } + }; + } + + virtual inline std::string_view GetShaderDefineName() override { return "EXP_HEIGHT_FOG"; } + bool HasShaderDefine(RE::BSShader::Type) override { return true; }; + + virtual void DrawSettings() override; + + virtual void RestoreDefaultSettings() override; + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + void RegisterWeatherVariables() override; + + struct alignas(16) Settings + { + uint enabled = 1; + uint useDynamicCubemaps = 0; + float startDistance = 0.0f; + float fogHeight = 0.0f; + float fogHeightFalloff = 0.2f; + float fogDensity = 0.02f; + float directionalInscatteringMultiplier = 1.0f; + float directionalInscatteringExponent = 4.0f; + float4 inscatteringTint = { 1.0f, 1.0f, 1.0f, 1.0f }; + float cubemapMipLevel = 3.0f; + float pad[3]; + } settings; }; \ No newline at end of file diff --git a/src/Features/ExtendedTranslucency.h b/src/Features/ExtendedTranslucency.h index 2f292eee92..d59f385f08 100644 --- a/src/Features/ExtendedTranslucency.h +++ b/src/Features/ExtendedTranslucency.h @@ -1,65 +1,62 @@ -#pragma once - -#include "../Buffer.h" -#include "../Feature.h" - -struct ExtendedTranslucency final : Feature -{ - static constexpr std::string_view MOD_ID = "150755"sv; - - virtual inline std::string GetName() override { return "Extended Translucency"; } - virtual inline std::string GetShortName() override { return "ExtendedTranslucency"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual inline std::string_view GetShaderDefineName() override { return "EXTENDED_TRANSLUCENCY"sv; } - virtual inline std::string_view GetCategory() const override { return FeatureCategories::kMaterials; } - virtual std::pair> GetFeatureSummary() override; - virtual bool HasShaderDefine(RE::BSShader::Type shaderType) override { return RE::BSShader::Type::Lighting == shaderType; }; - virtual void PostPostLoad() override; - virtual void DrawSettings() override; - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - virtual void RestoreDefaultSettings() override; - virtual bool SupportsVR() override { return true; }; - - static void BSLightingShader_SetupGeometry(RE::BSRenderPass* pass); - - struct Hooks; - - // TODO: Support more material model like glasses or arcylic - enum MaterialModel : uint32_t - { - Disabled = 0, // In user settings, 0 means 'Disabled' - RimLight = 1, // Similar effect like rim light - IsotropicFabric = 2, // 1D fabric model, respect normal map - AnisotropicFabric = 3, // 2D fabric model alone tangent and binormal, ignores normal map - - DescriptorUseDefault = 0, // In ExtraFeatureDescriptor, 0 means 'UseDefault' instead of 'Disabled' - DescriptorDisabled = 7, // In ExtraFeatureDescriptor, value >= 5 means 'Disabled' - }; - - static constexpr uint32_t ExtraFeatureDescriptorShift = 6; - static constexpr uint32_t ExtraFeatureDescriptorMask = 7; - - // Settings in both CPU and GPU constant buffer - struct alignas(16) PerFrame - { - uint32_t AlphaMode = MaterialModel::AnisotropicFabric; - float AlphaReduction = 0.15f; - float AlphaSoftness = 0.f; - float AlphaStrength = 0.f; - }; - - // Settings only in CPU - struct Settings : PerFrame - { - bool SkinnedOnly = true; - }; - - Settings settings; - - const PerFrame& GetCommonBufferData() { return settings; } - - static const RE::BSFixedString NiExtraDataName_AnisotropicAlphaMaterial; - - virtual bool IsCore() const override { return true; }; -}; +#pragma once + +#include "../Buffer.h" +#include "../Feature.h" + +struct ExtendedTranslucency final : Feature +{ + virtual inline std::string GetName() override { return "Extended Translucency"; } + virtual inline std::string GetShortName() override { return "ExtendedTranslucency"; } + virtual inline std::string_view GetShaderDefineName() override { return "EXTENDED_TRANSLUCENCY"sv; } + virtual inline std::string_view GetCategory() const override { return FeatureCategories::kMaterials; } + virtual std::pair> GetFeatureSummary() override; + virtual bool HasShaderDefine(RE::BSShader::Type shaderType) override { return RE::BSShader::Type::Lighting == shaderType; }; + virtual void PostPostLoad() override; + virtual void DrawSettings() override; + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + virtual void RestoreDefaultSettings() override; + virtual bool SupportsVR() override { return true; }; + + static void BSLightingShader_SetupGeometry(RE::BSRenderPass* pass); + + struct Hooks; + + // TODO: Support more material model like glasses or arcylic + enum MaterialModel : uint32_t + { + Disabled = 0, // In user settings, 0 means 'Disabled' + RimLight = 1, // Similar effect like rim light + IsotropicFabric = 2, // 1D fabric model, respect normal map + AnisotropicFabric = 3, // 2D fabric model alone tangent and binormal, ignores normal map + + DescriptorUseDefault = 0, // In ExtraFeatureDescriptor, 0 means 'UseDefault' instead of 'Disabled' + DescriptorDisabled = 7, // In ExtraFeatureDescriptor, value >= 5 means 'Disabled' + }; + + static constexpr uint32_t ExtraFeatureDescriptorShift = 6; + static constexpr uint32_t ExtraFeatureDescriptorMask = 7; + + // Settings in both CPU and GPU constant buffer + struct alignas(16) PerFrame + { + uint32_t AlphaMode = MaterialModel::AnisotropicFabric; + float AlphaReduction = 0.15f; + float AlphaSoftness = 0.f; + float AlphaStrength = 0.f; + }; + + // Settings only in CPU + struct Settings : PerFrame + { + bool SkinnedOnly = true; + }; + + Settings settings; + + const PerFrame& GetCommonBufferData() { return settings; } + + static const RE::BSFixedString NiExtraDataName_AnisotropicAlphaMaterial; + + virtual bool IsCore() const override { return true; }; +}; diff --git a/src/Features/GrassCollision.h b/src/Features/GrassCollision.h index 1cbb512e85..0a523dc0d1 100644 --- a/src/Features/GrassCollision.h +++ b/src/Features/GrassCollision.h @@ -1,107 +1,104 @@ -#pragma once - -#include "Buffer.h" - -struct GrassCollision : Feature -{ -private: - static constexpr std::string_view MOD_ID = "87816"; - -public: - virtual inline std::string GetName() override { return "Grass Collision"; } - virtual inline std::string GetShortName() override { return "GrassCollision"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual inline std::string_view GetShaderDefineName() override { return "GRASS_COLLISION"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kGrass; } - - virtual std::pair> GetFeatureSummary() override - { - return { - "Enables dynamic grass interactions where grass bends and moves in response to actors walking through it, creating more immersive environmental reactions.", - { "Real-time grass deformation from actor movement", - "Collision detection for up to 256 simultaneous interactions", - "Dynamic tracking of actor positions for grass response", - "Performance-optimized collision calculation", - "Seamless integration with existing grass rendering" } - }; - } - - bool HasShaderDefine(RE::BSShader::Type shaderType) override; - - void UpdateCollisionTexture(); - - struct Settings - { - bool EnableGrassCollision = 1; - bool TrackRagdolls = 1; - bool EnableBlur = 1; - }; - - struct alignas(16) BoundingBoxPacked - { - float2 MinExtent = { 0, 0 }; - float2 MaxExtent = { 0, 0 }; - uint IndexStart = 0; - uint IndexEnd = 0; - float2 pad0; - }; - STATIC_ASSERT_ALIGNAS_16(BoundingBoxPacked); - - struct alignas(16) PerFrame - { - float2 PosOffset; // cell origin in camera model space - DirectX::XMUINT2 ArrayOrigin; // xy: array origin (clipmap wrapping) - - DirectX::XMINT2 ValidMargin; - float TimeDelta; - uint BoundingBoxCount; - - float CameraHeightDelta; - float3 pad0; - }; - STATIC_ASSERT_ALIGNAS_16(PerFrame); - - Settings settings; - - ConstantBuffer* perFrame = nullptr; - - eastl::unique_ptr collisionBoundingBoxes = nullptr; - eastl::unique_ptr collisionInstances = nullptr; - - virtual void ClearShaderCache() override; - - ID3D11ComputeShader* GetCollisionUpdateCS(); - ID3D11ComputeShader* collisionUpdateCS; - - Texture2D* collisionTexture = nullptr; - - virtual void SetupResources() override; - - virtual void DrawSettings() override; - void UpdateCollisions(PerFrame& perFrame); - void Update(); - - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - - virtual void RestoreDefaultSettings() override; - - virtual void PostPostLoad() override; - - virtual bool SupportsVR() override { return true; }; - - struct Hooks - { - struct BSGrassShader_SetupGeometry - { - static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); - static inline REL::Relocation func; - }; - - static void Install() - { - stl::write_vfunc<0x6, BSGrassShader_SetupGeometry>(RE::VTABLE_BSGrassShader[0]); - logger::info("[GRASS COLLISION] Installed hooks"); - } - }; -}; +#pragma once + +#include "Buffer.h" + +struct GrassCollision : Feature +{ +private: +public: + virtual inline std::string GetName() override { return "Grass Collision"; } + virtual inline std::string GetShortName() override { return "GrassCollision"; } + virtual inline std::string_view GetShaderDefineName() override { return "GRASS_COLLISION"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kGrass; } + + virtual std::pair> GetFeatureSummary() override + { + return { + "Enables dynamic grass interactions where grass bends and moves in response to actors walking through it, creating more immersive environmental reactions.", + { "Real-time grass deformation from actor movement", + "Collision detection for up to 256 simultaneous interactions", + "Dynamic tracking of actor positions for grass response", + "Performance-optimized collision calculation", + "Seamless integration with existing grass rendering" } + }; + } + + bool HasShaderDefine(RE::BSShader::Type shaderType) override; + + void UpdateCollisionTexture(); + + struct Settings + { + bool EnableGrassCollision = 1; + bool TrackRagdolls = 1; + bool EnableBlur = 1; + }; + + struct alignas(16) BoundingBoxPacked + { + float2 MinExtent = { 0, 0 }; + float2 MaxExtent = { 0, 0 }; + uint IndexStart = 0; + uint IndexEnd = 0; + float2 pad0; + }; + STATIC_ASSERT_ALIGNAS_16(BoundingBoxPacked); + + struct alignas(16) PerFrame + { + float2 PosOffset; // cell origin in camera model space + DirectX::XMUINT2 ArrayOrigin; // xy: array origin (clipmap wrapping) + + DirectX::XMINT2 ValidMargin; + float TimeDelta; + uint BoundingBoxCount; + + float CameraHeightDelta; + float3 pad0; + }; + STATIC_ASSERT_ALIGNAS_16(PerFrame); + + Settings settings; + + ConstantBuffer* perFrame = nullptr; + + eastl::unique_ptr collisionBoundingBoxes = nullptr; + eastl::unique_ptr collisionInstances = nullptr; + + virtual void ClearShaderCache() override; + + ID3D11ComputeShader* GetCollisionUpdateCS(); + ID3D11ComputeShader* collisionUpdateCS; + + Texture2D* collisionTexture = nullptr; + + virtual void SetupResources() override; + + virtual void DrawSettings() override; + void UpdateCollisions(PerFrame& perFrame); + void Update(); + + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + virtual void RestoreDefaultSettings() override; + + virtual void PostPostLoad() override; + + virtual bool SupportsVR() override { return true; }; + + struct Hooks + { + struct BSGrassShader_SetupGeometry + { + static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); + static inline REL::Relocation func; + }; + + static void Install() + { + stl::write_vfunc<0x6, BSGrassShader_SetupGeometry>(RE::VTABLE_BSGrassShader[0]); + logger::info("[GRASS COLLISION] Installed hooks"); + } + }; +}; diff --git a/src/Features/GrassLighting.h b/src/Features/GrassLighting.h index e5fe72df2c..e07228802e 100644 --- a/src/Features/GrassLighting.h +++ b/src/Features/GrassLighting.h @@ -1,54 +1,51 @@ -#pragma once - -#include "Buffer.h" - -struct GrassLighting : Feature -{ -private: - static constexpr std::string_view MOD_ID = "86502"; - -public: - virtual inline std::string GetName() override { return "Grass Lighting"; } - virtual inline std::string GetShortName() override { return "GrassLighting"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual inline std::string_view GetShaderDefineName() override { return "GRASS_LIGHTING"; } - virtual bool HasShaderDefine(RE::BSShader::Type shaderType) override { return shaderType == RE::BSShader::Type::Grass; }; - virtual std::string_view GetCategory() const override { return FeatureCategories::kGrass; } - - virtual std::pair> GetFeatureSummary() override - { - return { - "Grass Lighting enhances grass rendering with improved lighting, specularity, and subsurface scattering.\n" - "This makes grass appear more natural and responsive to lighting conditions.", - { "Enhanced grass lighting model", - "Specular highlights on grass", - "Subsurface scattering effects", - "Improved grass visual quality", - "Configurable material properties" } - }; - } - - struct alignas(16) Settings - { - float Glossiness = 20.0f; - float SpecularStrength = 0.5f; - float SubsurfaceScatteringAmount = 0.5f; - uint OverrideComplexGrassSettings = false; - float BasicGrassBrightness = 1.0f; // Match brightness of ENB - uint EnableWrappedLighting = false; - float ComplexGrassThreshold = 0.03f; - uint pad1; - }; - STATIC_ASSERT_ALIGNAS_16(Settings); - - Settings settings; - - virtual void DrawSettings() override; - - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - - virtual void RestoreDefaultSettings() override; - - virtual bool SupportsVR() override { return true; }; -}; +#pragma once + +#include "Buffer.h" + +struct GrassLighting : Feature +{ +private: +public: + virtual inline std::string GetName() override { return "Grass Lighting"; } + virtual inline std::string GetShortName() override { return "GrassLighting"; } + virtual inline std::string_view GetShaderDefineName() override { return "GRASS_LIGHTING"; } + virtual bool HasShaderDefine(RE::BSShader::Type shaderType) override { return shaderType == RE::BSShader::Type::Grass; }; + virtual std::string_view GetCategory() const override { return FeatureCategories::kGrass; } + + virtual std::pair> GetFeatureSummary() override + { + return { + "Grass Lighting enhances grass rendering with improved lighting, specularity, and subsurface scattering.\n" + "This makes grass appear more natural and responsive to lighting conditions.", + { "Enhanced grass lighting model", + "Specular highlights on grass", + "Subsurface scattering effects", + "Improved grass visual quality", + "Configurable material properties" } + }; + } + + struct alignas(16) Settings + { + float Glossiness = 20.0f; + float SpecularStrength = 0.5f; + float SubsurfaceScatteringAmount = 0.5f; + uint OverrideComplexGrassSettings = false; + float BasicGrassBrightness = 1.0f; // Match brightness of ENB + uint EnableWrappedLighting = false; + float ComplexGrassThreshold = 0.03f; + uint pad1; + }; + STATIC_ASSERT_ALIGNAS_16(Settings); + + Settings settings; + + virtual void DrawSettings() override; + + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + virtual void RestoreDefaultSettings() override; + + virtual bool SupportsVR() override { return true; }; +}; diff --git a/src/Features/HairSpecular.h b/src/Features/HairSpecular.h index 5c7aeda8b9..7a5b410e05 100644 --- a/src/Features/HairSpecular.h +++ b/src/Features/HairSpecular.h @@ -1,63 +1,58 @@ -#pragma once - -struct HairSpecular : Feature -{ -private: - static constexpr std::string_view MOD_ID = "149011"; - -public: - virtual inline std::string GetName() override { return "Hair Specular"; } - virtual inline std::string GetShortName() override { return "HairSpecular"; } - virtual inline std::string_view GetShaderDefineName() override { return "CS_HAIR"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kCharacters; } - virtual std::pair> GetFeatureSummary() override - { - return { - "Provides better hair shading with realistic specular highlights and tangent-based light interaction for more lifelike hair appearance.", - { "Realistic hair specular highlights", - "Enhanced hair glossiness and saturation controls", - "Separate specular and diffuse lighting multipliers", - "Tangent shift texture support for varied hair highlights" } - }; - } - virtual bool HasShaderDefine(RE::BSShader::Type shaderType) override { return shaderType == RE::BSShader::Type::Lighting; }; - - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - - virtual void Prepass() override; - - virtual void SetupResources() override; - - struct alignas(16) Settings - { - uint Enabled = true; - float HairGlossiness = 70.0f; - float SpecularMult = 1.0f; - float DiffuseMult = 1.0f; - uint EnableTangentShift = true; - float PrimaryTangentShift = 0.5f; - float SecondaryTangentShift = -0.25f; - float HairSaturation = 1.0f; - float SpecularIndirectMult = 1.0f; - float DiffuseIndirectMult = 1.0f; - float BaseColorMult = 1.5f; - float Transmission = 1.0f; - uint EnableSelfShadow = true; - float SelfShadowStrength = 1.0f; - float SelfShadowExponent = 0.1f; - float SelfShadowScale = 2.5f; - uint HairMode = 1; // 0: Kajiya-Kay, 1: Marschner - uint pad[3]; - } settings; - - eastl::unique_ptr texTangentShift = nullptr; - - virtual void DrawSettings() override; - - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - - virtual void RestoreDefaultSettings() override; - - virtual bool SupportsVR() override { return true; }; +#pragma once + +struct HairSpecular : Feature +{ +private: +public: + virtual inline std::string GetName() override { return "Hair Specular"; } + virtual inline std::string GetShortName() override { return "HairSpecular"; } + virtual inline std::string_view GetShaderDefineName() override { return "CS_HAIR"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kCharacters; } + virtual std::pair> GetFeatureSummary() override + { + return { + "Provides better hair shading with realistic specular highlights and tangent-based light interaction for more lifelike hair appearance.", + { "Realistic hair specular highlights", + "Enhanced hair glossiness and saturation controls", + "Separate specular and diffuse lighting multipliers", + "Tangent shift texture support for varied hair highlights" } + }; + } + virtual bool HasShaderDefine(RE::BSShader::Type shaderType) override { return shaderType == RE::BSShader::Type::Lighting; }; + virtual void Prepass() override; + + virtual void SetupResources() override; + + struct alignas(16) Settings + { + uint Enabled = true; + float HairGlossiness = 70.0f; + float SpecularMult = 1.0f; + float DiffuseMult = 1.0f; + uint EnableTangentShift = true; + float PrimaryTangentShift = 0.5f; + float SecondaryTangentShift = -0.25f; + float HairSaturation = 1.0f; + float SpecularIndirectMult = 1.0f; + float DiffuseIndirectMult = 1.0f; + float BaseColorMult = 1.5f; + float Transmission = 1.0f; + uint EnableSelfShadow = true; + float SelfShadowStrength = 1.0f; + float SelfShadowExponent = 0.1f; + float SelfShadowScale = 2.5f; + uint HairMode = 1; // 0: Kajiya-Kay, 1: Marschner + uint pad[3]; + } settings; + + eastl::unique_ptr texTangentShift = nullptr; + + virtual void DrawSettings() override; + + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + virtual void RestoreDefaultSettings() override; + + virtual bool SupportsVR() override { return true; }; }; \ No newline at end of file diff --git a/src/Features/InteriorSun.h b/src/Features/InteriorSun.h index 9be4ef964a..3b61645dec 100644 --- a/src/Features/InteriorSun.h +++ b/src/Features/InteriorSun.h @@ -1,131 +1,129 @@ -#pragma once -#include "ShaderCache.h" - -struct InteriorSun : Feature -{ -private: - static constexpr std::string_view MOD_ID = "153541"; - -public: - virtual inline std::string GetName() override { return "Interior Sun"; } - virtual inline std::string GetShortName() override { return "InteriorSun"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } - virtual std::pair> GetFeatureSummary() override - { - return { - "Allows for the sun and moon to cast light and shadows into interior spaces.", - { "Functions only for explicitly enabled interiors", - "Utilizes existing sun, moon, and weather systems", - "Includes an option to force double-sided rendering for unprepared interiors", - "Fixes geometry culling issues that cause light leakage" } - }; - } - virtual void DrawSettings() override; - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - virtual void RestoreDefaultSettings() override; - virtual bool SupportsVR() override { return true; } - virtual void PostPostLoad() override; - virtual void EarlyPrepass() override; - - virtual void DataLoaded() override - { - MenuOpenCloseEventHandler::Register(); - } - - struct Settings - { - bool ForceDoubleSidedRendering = true; - float InteriorShadowDistance = 5000; - }; - - Settings settings; - - std::atomic isInteriorWithSun = false; - - struct GetWorldSpace - { - static RE::TESWorldSpace* thunk(RE::TES* tes); - static inline REL::Relocation func; - }; - - struct DirShadowLightCulling - { - static void thunk(RE::BSShadowDirectionalLight* dirLight, RE::BSTArray>>& jobArrays, RE::BSTArray>& nodes); - static inline REL::Relocation func; - }; - - struct BSBatchRenderer_RenderPassImmediately - { - static void thunk(RE::BSRenderPass* a_pass, uint32_t a_technique, bool a_alphaTest, uint32_t a_renderFlags); - static inline REL::Relocation func; - }; - - void UpdateRasterStateCullMode(const RE::BSRenderPass* pass, const uint32_t technique) const - { - if (isInteriorWithSun && settings.ForceDoubleSidedRendering && technique & static_cast(SIE::ShaderCache::UtilityShaderFlags::RenderShadowmap)) { - const auto flags = pass->shaderProperty->flags; - const auto renderTwoSided = flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kTwoSided) || flags.none(RE::BSShaderProperty::EShaderPropertyFlag::kAssumeShadowmask, RE::BSShaderProperty::EShaderPropertyFlag::kSkinned); - if (renderTwoSided && *rasterStateCullMode != 0) { - *rasterStateCullMode = 0; - globals::game::stateUpdateFlags->set(RE::BSGraphics::DIRTY_RASTER_CULL_MODE); - } else if (!renderTwoSided && *rasterStateCullMode != RE::BSGraphics::RasterStateCullMode::RASTER_STATE_CULL_MODE_BACK) { - *rasterStateCullMode = RE::BSGraphics::RasterStateCullMode::RASTER_STATE_CULL_MODE_BACK; - globals::game::stateUpdateFlags->set(RE::BSGraphics::DIRTY_RASTER_CULL_MODE); - } - } - } - - class MenuOpenCloseEventHandler : public RE::BSTEventSink - { - public: - virtual RE::BSEventNotifyControl ProcessEvent(const RE::MenuOpenCloseEvent* a_event, RE::BSTEventSource*); - - static bool Register() - { - static MenuOpenCloseEventHandler singleton; - auto ui = globals::game::ui; - if (!ui) { - logger::error("UI event source not found"); - return false; - } - ui->GetEventSource()->AddEventSink(&singleton); - - logger::info("Registered {}", typeid(singleton).name()); - return true; - } - }; - - static bool IsInteriorWithSun(const RE::TESObjectCELL* cell); - virtual bool IsCore() const override { return true; }; - -private: - enum class CellFlagExt : uint16_t - { - kSunlightShadows = 1 << 15, - }; - - float* gShadowDistance = nullptr; - float* gInteriorShadowDistance = nullptr; - uint32_t* rasterStateCullMode = nullptr; - - RE::TESObjectCELL* currentCell = nullptr; - - bool arraysCleared = true; - RE::BSTArray> currentCellRoomsAndPortals = {}; - RE::BSTArray>> replacementJobArrays = {}; - eastl::hash_set addedSet = {}; - - static RE::TESWorldSpace* enableInteriorSun; - static RE::TESWorldSpace* disableInteriorSun; - - void ClearArrays(); - - void InitialiseOnNewCell(const RE::NiPointer& portalGraph); - - bool IsInSunDirectionAndWithinShadowDistance(const RE::NiPointer& object, const RE::NiPoint3& lightDir, const RE::NiPoint3& playerPos) const; - - void PopulateReplacementJobArrays(RE::TESObjectCELL* cell, const RE::NiPointer& portalGraph, const RE::BSShadowDirectionalLight* dirLight, RE::BSTArray>>& jobArrays); - - static void SetShadowDistance(bool inInterior); +#pragma once +#include "ShaderCache.h" + +struct InteriorSun : Feature +{ +private: +public: + virtual inline std::string GetName() override { return "Interior Sun"; } + virtual inline std::string GetShortName() override { return "InteriorSun"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } + virtual std::pair> GetFeatureSummary() override + { + return { + "Allows for the sun and moon to cast light and shadows into interior spaces.", + { "Functions only for explicitly enabled interiors", + "Utilizes existing sun, moon, and weather systems", + "Includes an option to force double-sided rendering for unprepared interiors", + "Fixes geometry culling issues that cause light leakage" } + }; + } + virtual void DrawSettings() override; + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + virtual void RestoreDefaultSettings() override; + virtual bool SupportsVR() override { return true; } + virtual void PostPostLoad() override; + virtual void EarlyPrepass() override; + + virtual void DataLoaded() override + { + MenuOpenCloseEventHandler::Register(); + } + + struct Settings + { + bool ForceDoubleSidedRendering = true; + float InteriorShadowDistance = 5000; + }; + + Settings settings; + + std::atomic isInteriorWithSun = false; + + struct GetWorldSpace + { + static RE::TESWorldSpace* thunk(RE::TES* tes); + static inline REL::Relocation func; + }; + + struct DirShadowLightCulling + { + static void thunk(RE::BSShadowDirectionalLight* dirLight, RE::BSTArray>>& jobArrays, RE::BSTArray>& nodes); + static inline REL::Relocation func; + }; + + struct BSBatchRenderer_RenderPassImmediately + { + static void thunk(RE::BSRenderPass* a_pass, uint32_t a_technique, bool a_alphaTest, uint32_t a_renderFlags); + static inline REL::Relocation func; + }; + + void UpdateRasterStateCullMode(const RE::BSRenderPass* pass, const uint32_t technique) const + { + if (isInteriorWithSun && settings.ForceDoubleSidedRendering && technique & static_cast(SIE::ShaderCache::UtilityShaderFlags::RenderShadowmap)) { + const auto flags = pass->shaderProperty->flags; + const auto renderTwoSided = flags.all(RE::BSShaderProperty::EShaderPropertyFlag::kTwoSided) || flags.none(RE::BSShaderProperty::EShaderPropertyFlag::kAssumeShadowmask, RE::BSShaderProperty::EShaderPropertyFlag::kSkinned); + if (renderTwoSided && *rasterStateCullMode != 0) { + *rasterStateCullMode = 0; + globals::game::stateUpdateFlags->set(RE::BSGraphics::DIRTY_RASTER_CULL_MODE); + } else if (!renderTwoSided && *rasterStateCullMode != RE::BSGraphics::RasterStateCullMode::RASTER_STATE_CULL_MODE_BACK) { + *rasterStateCullMode = RE::BSGraphics::RasterStateCullMode::RASTER_STATE_CULL_MODE_BACK; + globals::game::stateUpdateFlags->set(RE::BSGraphics::DIRTY_RASTER_CULL_MODE); + } + } + } + + class MenuOpenCloseEventHandler : public RE::BSTEventSink + { + public: + virtual RE::BSEventNotifyControl ProcessEvent(const RE::MenuOpenCloseEvent* a_event, RE::BSTEventSource*); + + static bool Register() + { + static MenuOpenCloseEventHandler singleton; + auto ui = globals::game::ui; + if (!ui) { + logger::error("UI event source not found"); + return false; + } + ui->GetEventSource()->AddEventSink(&singleton); + + logger::info("Registered {}", typeid(singleton).name()); + return true; + } + }; + + static bool IsInteriorWithSun(const RE::TESObjectCELL* cell); + virtual bool IsCore() const override { return true; }; + +private: + enum class CellFlagExt : uint16_t + { + kSunlightShadows = 1 << 15, + }; + + float* gShadowDistance = nullptr; + float* gInteriorShadowDistance = nullptr; + uint32_t* rasterStateCullMode = nullptr; + + RE::TESObjectCELL* currentCell = nullptr; + + bool arraysCleared = true; + RE::BSTArray> currentCellRoomsAndPortals = {}; + RE::BSTArray>> replacementJobArrays = {}; + eastl::hash_set addedSet = {}; + + static RE::TESWorldSpace* enableInteriorSun; + static RE::TESWorldSpace* disableInteriorSun; + + void ClearArrays(); + + void InitialiseOnNewCell(const RE::NiPointer& portalGraph); + + bool IsInSunDirectionAndWithinShadowDistance(const RE::NiPointer& object, const RE::NiPoint3& lightDir, const RE::NiPoint3& playerPos) const; + + void PopulateReplacementJobArrays(RE::TESObjectCELL* cell, const RE::NiPointer& portalGraph, const RE::BSShadowDirectionalLight* dirLight, RE::BSTArray>>& jobArrays); + + static void SetShadowDistance(bool inInterior); }; \ No newline at end of file diff --git a/src/Features/InverseSquareLighting.h b/src/Features/InverseSquareLighting.h index a0b5ba0ae1..65e77971bc 100644 --- a/src/Features/InverseSquareLighting.h +++ b/src/Features/InverseSquareLighting.h @@ -1,76 +1,74 @@ -#pragma once -#include "Features/InverseSquareLighting/LightEditor.h" -#include "LightLimitFix.h" - -struct InverseSquareLighting : Feature -{ -private: - static constexpr std::string_view MOD_ID = "153542"; - -public: - virtual inline std::string GetName() override { return "Inverse Square Lighting"; } - - virtual inline std::string GetShortName() override { return "InverseSquareLighting"; } - - virtual inline std::string_view GetShaderDefineName() override { return "ISL"; } - - virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } - - virtual std::pair> GetFeatureSummary() override - { - return { - "Implements an additional inverse square falloff for lighting which allows for a more physically accurate and realistic looking light attenuation.", - { "Automatic light radius calculation based on intensity", - "Lights smoothly fade out at a configurable cutoff, solving the infinite distance problem", - "Does not modify any existing lighting", - "Requires the use of mods with lights enabled for inverse square falloff.", - "Full integration with Light Placer", - "Built in Light Editor for mod authors to preview lighting changes in real-time" } - }; - } - - inline bool HasShaderDefine(RE::BSShader::Type) override { return true; }; - - virtual void DrawSettings() override; - - virtual void EarlyPrepass() override; - - virtual bool SupportsVR() override { return true; } - - virtual void PostPostLoad() override; - - static float CalculateRadius(float intensity, bool shadowCaster, float cutoffOverride, float size); - - void ProcessLight(LightLimitFix::LightData& light, RE::BSLight* bsLight, RE::NiLight* niLight) const; - - static float GetAttenuation(float distance, float radius, float size); - - struct CreatePointLight - { - static RE::NiPointLight* thunk(RE::TESObjectLIGH* ligh, RE::TESObjectREFR* refr, RE::NiAVObject* root, bool forceDynamic, bool useLightRadius, bool affectRequesterOnly); - static inline REL::Relocation func; - }; - - struct BSLight_GetLuminance - { - static float thunk(RE::BSLight* bsLight, RE::NiPoint3* targetPosition, RE::NiLight* refLight); - static inline REL::Relocation func; - }; - virtual bool IsCore() const override { return true; }; - -private: - LightEditor editor = LightEditor(); - - static constexpr float DefaultCutoff = 0.05f; - static constexpr float DefaultShadowCasterCutoff = 0.022f; - - static constexpr float Scale = 0.8f; - static constexpr float MetresToUnits = 70.f; - static constexpr float MetresToUnitsSq = MetresToUnits * MetresToUnits; - static constexpr float ScaledUnitsSq = Scale * MetresToUnitsSq; - static constexpr float FadeZoneBase = 4.5f * Scale * MetresToUnits; - - static void SetExtLightData(RE::NiLight* niLight, const RE::TESObjectLIGH* ligh); - - static inline float SmoothStep(float edge0, float edge1, float x); +#pragma once +#include "Features/InverseSquareLighting/LightEditor.h" +#include "LightLimitFix.h" + +struct InverseSquareLighting : Feature +{ +private: +public: + virtual inline std::string GetName() override { return "Inverse Square Lighting"; } + + virtual inline std::string GetShortName() override { return "InverseSquareLighting"; } + + virtual inline std::string_view GetShaderDefineName() override { return "ISL"; } + + virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } + + virtual std::pair> GetFeatureSummary() override + { + return { + "Implements an additional inverse square falloff for lighting which allows for a more physically accurate and realistic looking light attenuation.", + { "Automatic light radius calculation based on intensity", + "Lights smoothly fade out at a configurable cutoff, solving the infinite distance problem", + "Does not modify any existing lighting", + "Requires the use of mods with lights enabled for inverse square falloff.", + "Full integration with Light Placer", + "Built in Light Editor for mod authors to preview lighting changes in real-time" } + }; + } + + inline bool HasShaderDefine(RE::BSShader::Type) override { return true; }; + + virtual void DrawSettings() override; + + virtual void EarlyPrepass() override; + + virtual bool SupportsVR() override { return true; } + + virtual void PostPostLoad() override; + + static float CalculateRadius(float intensity, bool shadowCaster, float cutoffOverride, float size); + + void ProcessLight(LightLimitFix::LightData& light, RE::BSLight* bsLight, RE::NiLight* niLight) const; + + static float GetAttenuation(float distance, float radius, float size); + + struct CreatePointLight + { + static RE::NiPointLight* thunk(RE::TESObjectLIGH* ligh, RE::TESObjectREFR* refr, RE::NiAVObject* root, bool forceDynamic, bool useLightRadius, bool affectRequesterOnly); + static inline REL::Relocation func; + }; + + struct BSLight_GetLuminance + { + static float thunk(RE::BSLight* bsLight, RE::NiPoint3* targetPosition, RE::NiLight* refLight); + static inline REL::Relocation func; + }; + virtual bool IsCore() const override { return true; }; + +private: + LightEditor editor = LightEditor(); + + static constexpr float DefaultCutoff = 0.05f; + static constexpr float DefaultShadowCasterCutoff = 0.022f; + + static constexpr float Scale = 0.8f; + static constexpr float MetresToUnits = 70.f; + static constexpr float MetresToUnitsSq = MetresToUnits * MetresToUnits; + static constexpr float ScaledUnitsSq = Scale * MetresToUnitsSq; + static constexpr float FadeZoneBase = 4.5f * Scale * MetresToUnits; + + static void SetExtLightData(RE::NiLight* niLight, const RE::TESObjectLIGH* ligh); + + static inline float SmoothStep(float edge0, float edge1, float x); }; \ No newline at end of file diff --git a/src/Features/LightLimitFix.h b/src/Features/LightLimitFix.h index 8b14963b72..e286061882 100644 --- a/src/Features/LightLimitFix.h +++ b/src/Features/LightLimitFix.h @@ -1,276 +1,273 @@ -#pragma once - -#include "Buffer.h" -#include "OverlayFeature.h" - -struct LightLimitFix : OverlayFeature -{ -private: - static constexpr std::string_view MOD_ID = "99548"; - -public: - virtual inline std::string GetName() override { return "Light Limit Fix"; } - virtual inline std::string GetShortName() override { return "LightLimitFix"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual inline std::string_view GetShaderDefineName() override { return "LIGHT_LIMIT_FIX"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } - - virtual std::pair> GetFeatureSummary() override - { - return { - "Light Limit Fix removes the vanilla game's 4-light limit, allowing unlimited dynamic lights in scenes.\n" - "This dramatically improves lighting quality and enables more realistic illumination scenarios.", - { "Removes 4-light limit", - "Unlimited dynamic lights", - "Improved lighting quality", - "Enhanced visual realism", - "Enhanced visual realism" } - }; - } - - bool HasShaderDefine(RE::BSShader::Type) override { return true; }; - - enum class LightFlags : std::uint32_t - { - PortalStrict = (1 << 0), - Shadow = (1 << 1), - Simple = (1 << 2), - - Initialised = (1 << 8), - Disabled = (1 << 9), - InverseSquare = (1 << 10), - Linear = (1 << 11), - }; - - struct PositionOpt - { - float3 data; - uint pad0; - }; - - struct alignas(16) LightData - { - float3 color; - float fade = 1.0f; - float radius; - float invRadius; - float fadeZone; - float sizeBias; - PositionOpt positionWS[2]; - uint128_t roomFlags = uint32_t(0); - stl::enumeration lightFlags; - uint32_t shadowMaskIndex = 0; - uint pad0; - uint pad1; - }; - STATIC_ASSERT_ALIGNAS_16(LightData); - - struct ClusterAABB - { - float4 minPoint; - float4 maxPoint; - }; - - struct alignas(16) LightGrid - { - uint offset; - uint lightCount; - uint pad0[2]; - }; - STATIC_ASSERT_ALIGNAS_16(LightGrid); - - struct alignas(16) LightBuildingCB - { - float LightsNear; - float LightsFar; - uint pad0[2]; - uint ClusterSize[4]; - }; - STATIC_ASSERT_ALIGNAS_16(LightBuildingCB); - - struct alignas(16) LightCullingCB - { - uint LightCount; - uint pad[3]; - uint ClusterSize[4]; - }; - STATIC_ASSERT_ALIGNAS_16(LightCullingCB); - - struct alignas(16) PerFrame - { - uint EnableLightsVisualisation; - uint LightsVisualisationMode; - float pad0[2]; - uint ClusterSize[4]; - }; - STATIC_ASSERT_ALIGNAS_16(PerFrame); - - PerFrame GetCommonBufferData(); - - struct alignas(16) StrictLightDataCB - { - uint NumStrictLights; - int RoomIndex; - uint ShadowBitMask; - uint pad0; - LightData StrictLights[15]; - }; - STATIC_ASSERT_ALIGNAS_16(StrictLightDataCB); - - StrictLightDataCB strictLightDataTemp; - - ConstantBuffer* strictLightDataCB = nullptr; - - int eyeCount = !REL::Module::IsVR() ? 1 : 2; - bool previousEnableLightsVisualisation = settings.EnableLightsVisualisation; - bool currentEnableLightsVisualisation = settings.EnableLightsVisualisation; - - ID3D11ComputeShader* clusterBuildingCS = nullptr; - ID3D11ComputeShader* clusterCullingCS = nullptr; - - ConstantBuffer* lightBuildingCB = nullptr; - ConstantBuffer* lightCullingCB = nullptr; - - eastl::unique_ptr lights = nullptr; - eastl::unique_ptr clusters = nullptr; - eastl::unique_ptr lightIndexCounter = nullptr; - eastl::unique_ptr lightIndexList = nullptr; - eastl::unique_ptr lightGrid = nullptr; - - std::uint32_t lightCount = 0; - float lightsNear = 1; - float lightsFar = 16384; - - RE::NiPoint3 eyePositionCached[2]{}; - bool wasEmpty = false; - bool wasWorld = false; - int previousRoomIndex = -1; - uint previousShadowBitMask = 0; - - Util::FrameChecker frameChecker; - - virtual void SetupResources() override; - - virtual void RestoreDefaultSettings() override; - - virtual void DrawSettings() override; - virtual void DrawOverlay() override; - virtual bool IsOverlayVisible() const override { return settings.EnableLightsVisualisation; } - - virtual void PostPostLoad() override; - virtual void DataLoaded() override; - virtual void ClearShaderCache() override; - - float CalculateLightDistance(float3 a_lightPosition, float a_radius); - void SetLightPosition(LightLimitFix::LightData& a_light, RE::NiPoint3 a_initialPosition, bool a_cached = true); - void UpdateLights(); - void UpdateStructure(); - virtual void Prepass() override; - - static inline float3 Saturation(float3 color, float saturation); - static inline bool IsValidLight(RE::BSLight* a_light); - static inline bool IsGlobalLight(RE::BSLight* a_light); - - struct Settings - { - bool EnableLightsVisualisation = false; - uint LightsVisualisationMode = 0; - }; - - uint clusterSize[3] = { 16 }; - - Settings settings; - - void BSLightingShader_SetupGeometry_Before(RE::BSRenderPass* a_pass); - - void BSLightingShader_SetupGeometry_GeometrySetupConstantPointLights(RE::BSRenderPass* a_pass); - - void BSLightingShader_SetupGeometry_After(RE::BSRenderPass* a_pass); - - eastl::hash_map roomNodes; - - struct Hooks - { - struct BSLightingShader_SetupGeometry - { - static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); - static inline REL::Relocation func; - }; - - struct BSEffectShader_SetupGeometry - { - static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); - static inline REL::Relocation func; - }; - - struct BSWaterShader_SetupGeometry - { - static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); - static inline REL::Relocation func; - }; - - template - struct ValidLight - { - static bool thunk(RE::BSShaderProperty* a_property, RE::BSLight* a_light) - { - return func(a_property, a_light) && (a_light->portalStrict || !a_light->portalGraph || a_light->IsShadowLight()); - } - static inline REL::Relocation func; - }; - - using ValidLight1 = ValidLight<1>; - using ValidLight2 = ValidLight<2>; - using ValidLight3 = ValidLight<3>; - - static void Install() - { - stl::write_vfunc<0x6, BSLightingShader_SetupGeometry>(RE::VTABLE_BSLightingShader[0]); - stl::write_vfunc<0x6, BSEffectShader_SetupGeometry>(RE::VTABLE_BSEffectShader[0]); - stl::write_vfunc<0x6, BSWaterShader_SetupGeometry>(RE::VTABLE_BSWaterShader[0]); - - stl::write_thunk_call(REL::RelocationID(100994, 107781).address() + 0x92); - stl::write_thunk_call(REL::RelocationID(100997, 107784).address() + REL::Relocate(0x139, 0x12A, 0x133)); - stl::write_thunk_call(REL::RelocationID(101296, 108283).address() + REL::Relocate(0xB7, 0x7E)); - - logger::info("[LLF] Installed hooks"); - } - }; - - virtual bool SupportsVR() override { return true; }; - virtual bool IsCore() const override { return true; } -}; - -template <> -struct fmt::formatter -{ - // Presentation format: 'f' - fixed. - char presentation = 'f'; - - // Parses format specifications of the form ['f']. - constexpr auto parse(format_parse_context& ctx) -> format_parse_context::iterator - { - auto it = ctx.begin(), end = ctx.end(); - if (it != end && (*it == 'f')) - presentation = *it++; - - // Check if reached the end of the range: - if (it != end && *it != '}') - throw format_error("invalid format"); - - // Return an iterator past the end of the parsed range: - return it; - } - - // Formats the point p using the parsed format specification (presentation) - // stored in this formatter. - auto format(const LightLimitFix::LightData& l, format_context& ctx) const -> format_context::iterator - { - // ctx.out() is an output iterator to write to. - return fmt::format_to(ctx.out(), "{{address {:x} color {} radius {} posWS {} {}}}", - reinterpret_cast(&l), - (Vector3)l.color, - l.radius, - (Vector3)l.positionWS[0].data, (Vector3)l.positionWS[1].data); - } -}; +#pragma once + +#include "Buffer.h" +#include "OverlayFeature.h" + +struct LightLimitFix : OverlayFeature +{ +private: +public: + virtual inline std::string GetName() override { return "Light Limit Fix"; } + virtual inline std::string GetShortName() override { return "LightLimitFix"; } + virtual inline std::string_view GetShaderDefineName() override { return "LIGHT_LIMIT_FIX"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } + + virtual std::pair> GetFeatureSummary() override + { + return { + "Light Limit Fix removes the vanilla game's 4-light limit, allowing unlimited dynamic lights in scenes.\n" + "This dramatically improves lighting quality and enables more realistic illumination scenarios.", + { "Removes 4-light limit", + "Unlimited dynamic lights", + "Improved lighting quality", + "Enhanced visual realism", + "Enhanced visual realism" } + }; + } + + bool HasShaderDefine(RE::BSShader::Type) override { return true; }; + + enum class LightFlags : std::uint32_t + { + PortalStrict = (1 << 0), + Shadow = (1 << 1), + Simple = (1 << 2), + + Initialised = (1 << 8), + Disabled = (1 << 9), + InverseSquare = (1 << 10), + Linear = (1 << 11), + }; + + struct PositionOpt + { + float3 data; + uint pad0; + }; + + struct alignas(16) LightData + { + float3 color; + float fade = 1.0f; + float radius; + float invRadius; + float fadeZone; + float sizeBias; + PositionOpt positionWS[2]; + uint128_t roomFlags = uint32_t(0); + stl::enumeration lightFlags; + uint32_t shadowMaskIndex = 0; + uint pad0; + uint pad1; + }; + STATIC_ASSERT_ALIGNAS_16(LightData); + + struct ClusterAABB + { + float4 minPoint; + float4 maxPoint; + }; + + struct alignas(16) LightGrid + { + uint offset; + uint lightCount; + uint pad0[2]; + }; + STATIC_ASSERT_ALIGNAS_16(LightGrid); + + struct alignas(16) LightBuildingCB + { + float LightsNear; + float LightsFar; + uint pad0[2]; + uint ClusterSize[4]; + }; + STATIC_ASSERT_ALIGNAS_16(LightBuildingCB); + + struct alignas(16) LightCullingCB + { + uint LightCount; + uint pad[3]; + uint ClusterSize[4]; + }; + STATIC_ASSERT_ALIGNAS_16(LightCullingCB); + + struct alignas(16) PerFrame + { + uint EnableLightsVisualisation; + uint LightsVisualisationMode; + float pad0[2]; + uint ClusterSize[4]; + }; + STATIC_ASSERT_ALIGNAS_16(PerFrame); + + PerFrame GetCommonBufferData(); + + struct alignas(16) StrictLightDataCB + { + uint NumStrictLights; + int RoomIndex; + uint ShadowBitMask; + uint pad0; + LightData StrictLights[15]; + }; + STATIC_ASSERT_ALIGNAS_16(StrictLightDataCB); + + StrictLightDataCB strictLightDataTemp; + + ConstantBuffer* strictLightDataCB = nullptr; + + int eyeCount = !REL::Module::IsVR() ? 1 : 2; + bool previousEnableLightsVisualisation = settings.EnableLightsVisualisation; + bool currentEnableLightsVisualisation = settings.EnableLightsVisualisation; + + ID3D11ComputeShader* clusterBuildingCS = nullptr; + ID3D11ComputeShader* clusterCullingCS = nullptr; + + ConstantBuffer* lightBuildingCB = nullptr; + ConstantBuffer* lightCullingCB = nullptr; + + eastl::unique_ptr lights = nullptr; + eastl::unique_ptr clusters = nullptr; + eastl::unique_ptr lightIndexCounter = nullptr; + eastl::unique_ptr lightIndexList = nullptr; + eastl::unique_ptr lightGrid = nullptr; + + std::uint32_t lightCount = 0; + float lightsNear = 1; + float lightsFar = 16384; + + RE::NiPoint3 eyePositionCached[2]{}; + bool wasEmpty = false; + bool wasWorld = false; + int previousRoomIndex = -1; + uint previousShadowBitMask = 0; + + Util::FrameChecker frameChecker; + + virtual void SetupResources() override; + + virtual void RestoreDefaultSettings() override; + + virtual void DrawSettings() override; + virtual void DrawOverlay() override; + virtual bool IsOverlayVisible() const override { return settings.EnableLightsVisualisation; } + + virtual void PostPostLoad() override; + virtual void DataLoaded() override; + virtual void ClearShaderCache() override; + + float CalculateLightDistance(float3 a_lightPosition, float a_radius); + void SetLightPosition(LightLimitFix::LightData& a_light, RE::NiPoint3 a_initialPosition, bool a_cached = true); + void UpdateLights(); + void UpdateStructure(); + virtual void Prepass() override; + + static inline float3 Saturation(float3 color, float saturation); + static inline bool IsValidLight(RE::BSLight* a_light); + static inline bool IsGlobalLight(RE::BSLight* a_light); + + struct Settings + { + bool EnableLightsVisualisation = false; + uint LightsVisualisationMode = 0; + }; + + uint clusterSize[3] = { 16 }; + + Settings settings; + + void BSLightingShader_SetupGeometry_Before(RE::BSRenderPass* a_pass); + + void BSLightingShader_SetupGeometry_GeometrySetupConstantPointLights(RE::BSRenderPass* a_pass); + + void BSLightingShader_SetupGeometry_After(RE::BSRenderPass* a_pass); + + eastl::hash_map roomNodes; + + struct Hooks + { + struct BSLightingShader_SetupGeometry + { + static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); + static inline REL::Relocation func; + }; + + struct BSEffectShader_SetupGeometry + { + static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); + static inline REL::Relocation func; + }; + + struct BSWaterShader_SetupGeometry + { + static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); + static inline REL::Relocation func; + }; + + template + struct ValidLight + { + static bool thunk(RE::BSShaderProperty* a_property, RE::BSLight* a_light) + { + return func(a_property, a_light) && (a_light->portalStrict || !a_light->portalGraph || a_light->IsShadowLight()); + } + static inline REL::Relocation func; + }; + + using ValidLight1 = ValidLight<1>; + using ValidLight2 = ValidLight<2>; + using ValidLight3 = ValidLight<3>; + + static void Install() + { + stl::write_vfunc<0x6, BSLightingShader_SetupGeometry>(RE::VTABLE_BSLightingShader[0]); + stl::write_vfunc<0x6, BSEffectShader_SetupGeometry>(RE::VTABLE_BSEffectShader[0]); + stl::write_vfunc<0x6, BSWaterShader_SetupGeometry>(RE::VTABLE_BSWaterShader[0]); + + stl::write_thunk_call(REL::RelocationID(100994, 107781).address() + 0x92); + stl::write_thunk_call(REL::RelocationID(100997, 107784).address() + REL::Relocate(0x139, 0x12A, 0x133)); + stl::write_thunk_call(REL::RelocationID(101296, 108283).address() + REL::Relocate(0xB7, 0x7E)); + + logger::info("[LLF] Installed hooks"); + } + }; + + virtual bool SupportsVR() override { return true; }; + virtual bool IsCore() const override { return true; } +}; + +template <> +struct fmt::formatter +{ + // Presentation format: 'f' - fixed. + char presentation = 'f'; + + // Parses format specifications of the form ['f']. + constexpr auto parse(format_parse_context& ctx) -> format_parse_context::iterator + { + auto it = ctx.begin(), end = ctx.end(); + if (it != end && (*it == 'f')) + presentation = *it++; + + // Check if reached the end of the range: + if (it != end && *it != '}') + throw format_error("invalid format"); + + // Return an iterator past the end of the parsed range: + return it; + } + + // Formats the point p using the parsed format specification (presentation) + // stored in this formatter. + auto format(const LightLimitFix::LightData& l, format_context& ctx) const -> format_context::iterator + { + // ctx.out() is an output iterator to write to. + return fmt::format_to(ctx.out(), "{{address {:x} color {} radius {} posWS {} {}}}", + reinterpret_cast(&l), + (Vector3)l.color, + l.radius, + (Vector3)l.positionWS[0].data, (Vector3)l.positionWS[1].data); + } +}; diff --git a/src/Features/ScreenSpaceGI.h b/src/Features/ScreenSpaceGI.h index ed5147b80b..bf7ca09082 100644 --- a/src/Features/ScreenSpaceGI.h +++ b/src/Features/ScreenSpaceGI.h @@ -1,169 +1,166 @@ -#pragma once - -#include "Buffer.h" - -struct ScreenSpaceGI : Feature -{ -private: - static constexpr std::string_view MOD_ID = "130375"; - -public: - bool inline SupportsVR() override { return true; } - - virtual inline std::string GetName() override { return "Screen Space GI"; } - virtual inline std::string GetShortName() override { return "ScreenSpaceGI"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } - - virtual std::pair> GetFeatureSummary() override - { - std::string desc = - "Screen Space Global Illumination adds realistic indirect lighting and " - "ambient occlusion to the game. This technique simulates how light " - "bounces off surfaces to illuminate other objects naturally."; - if (REL::Module::IsVR()) { - desc += - "\n\nWarning: In VR, this feature may have visual artifacts and " - "can have a significant performance impact due to the nature of " - "screen space effects."; - } - return std::make_pair( - desc, - std::vector{ - "Realistic indirect lighting", - "Enhanced ambient occlusion", - "Improved visual depth and atmosphere", - "Temporal denoising for smooth results", - "Configurable quality and performance settings" }); - } - - virtual void RestoreDefaultSettings() override; - virtual void DrawSettings() override; - - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - - virtual void SetupResources() override; - virtual void ClearShaderCache() override; - void CompileComputeShaders(); - bool ShadersOK(); - - void DrawSSGI(); - void UpdateSB(); - - ////////////////////////////////////////////////////////////////////////////////// - - bool recompileFlag = false; - uint outputAoIdx = 0; - uint outputIlIdx = 0; - - struct Settings - { - bool Enabled = true; - bool EnableGI = REL::Module::IsVR() ? false : true; // AO only for VR by default - bool EnableExperimentalSpecularGI = false; - bool EnableVanillaSSAO = false; - // performance/quality - uint NumSlices = REL::Module::IsVR() ? 3u : 4u; // AO preset for VR - uint NumSteps = REL::Module::IsVR() ? 6u : 8u; - int ResolutionMode = 1; // 0-full, 1-half, 2-quarter - DBF default - // visual - float MinScreenRadius = 0.01f; - float AORadius = 256.f; - float GIRadius = 256.f; - float Thickness = 32.f; - float2 DepthFadeRange = { 4e4, 5e4 }; - // gi - float GISaturation = 0.8f; - float GIDistanceCompensation = 0.f; - // mix - float AOPower = 1.0f; - float GIStrength = 1.0f; - // denoise - bool EnableTemporalDenoiser = true; - bool EnableBlur = true; - float DepthDisocclusion = .1f; - float NormalDisocclusion = .1f; - uint MaxAccumFrames = 16; - float BlurRadius = 2.f; - float DistanceNormalisation = 2.f; - } settings; - - struct alignas(16) SSGICB - { - float4x4 PrevInvViewMat[2]; - float2 NDCToViewMul[2]; - float2 NDCToViewAdd[2]; - - float2 TexDim; - float2 RcpTexDim; // - float2 FrameDim; - float2 RcpFrameDim; // - uint FrameIndex; - - uint NumSlices; - uint NumSteps; - - float MinScreenRadius; // - float AORadius; - float GIRadius; - float EffectRadius; - float Thickness; // - float2 DepthFadeRange; - float DepthFadeScaleConst; - - float GISaturation; // - float GIDistanceCompensation; - float GICompensationMaxDist; - float pad1; - - float AOPower; // - float GIStrength; - - float DepthDisocclusion; - float NormalDisocclusion; - uint MaxAccumFrames; // - - float BlurRadius; - float DistanceNormalisation; - - float2 pad; - }; - STATIC_ASSERT_ALIGNAS_16(SSGICB); - eastl::unique_ptr ssgiCB; - - eastl::unique_ptr texNoise = nullptr; - eastl::unique_ptr texWorkingDepth = nullptr; - winrt::com_ptr uavWorkingDepth[5] = { nullptr }; - eastl::unique_ptr texPrevGeo = nullptr; - eastl::unique_ptr texRadiance = nullptr; - eastl::unique_ptr texRadianceTemp = nullptr; - winrt::com_ptr uavRadiance[5] = { nullptr }; - eastl::unique_ptr texAccumFrames[2] = { nullptr }; - eastl::unique_ptr texAo[2] = { nullptr }; - eastl::unique_ptr texIlY[2] = { nullptr }; - eastl::unique_ptr texIlCoCg[2] = { nullptr }; - eastl::unique_ptr texGiSpecular[2] = { nullptr }; - - inline auto GetOutputTextures() - { - return (loaded && settings.Enabled) ? - std::make_tuple( - texAo[outputAoIdx]->srv.get(), - texIlY[outputIlIdx]->srv.get(), - texIlCoCg[outputIlIdx]->srv.get(), - texGiSpecular[outputAoIdx]->srv.get()) : - std::make_tuple(nullptr, nullptr, nullptr, nullptr); - } - - winrt::com_ptr linearClampSampler = nullptr; - winrt::com_ptr pointClampSampler = nullptr; - - winrt::com_ptr prefilterDepthsCompute = nullptr; - winrt::com_ptr prefilterRadianceCompute = nullptr; - winrt::com_ptr radianceDisoccCompute = nullptr; - winrt::com_ptr giCompute = nullptr; - winrt::com_ptr blurCompute = nullptr; - winrt::com_ptr stereoSyncCompute = nullptr; - winrt::com_ptr upsampleCompute = nullptr; -}; +#pragma once + +#include "Buffer.h" + +struct ScreenSpaceGI : Feature +{ +private: +public: + bool inline SupportsVR() override { return true; } + + virtual inline std::string GetName() override { return "Screen Space GI"; } + virtual inline std::string GetShortName() override { return "ScreenSpaceGI"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } + + virtual std::pair> GetFeatureSummary() override + { + std::string desc = + "Screen Space Global Illumination adds realistic indirect lighting and " + "ambient occlusion to the game. This technique simulates how light " + "bounces off surfaces to illuminate other objects naturally."; + if (REL::Module::IsVR()) { + desc += + "\n\nWarning: In VR, this feature may have visual artifacts and " + "can have a significant performance impact due to the nature of " + "screen space effects."; + } + return std::make_pair( + desc, + std::vector{ + "Realistic indirect lighting", + "Enhanced ambient occlusion", + "Improved visual depth and atmosphere", + "Temporal denoising for smooth results", + "Configurable quality and performance settings" }); + } + + virtual void RestoreDefaultSettings() override; + virtual void DrawSettings() override; + + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + virtual void SetupResources() override; + virtual void ClearShaderCache() override; + void CompileComputeShaders(); + bool ShadersOK(); + + void DrawSSGI(); + void UpdateSB(); + + ////////////////////////////////////////////////////////////////////////////////// + + bool recompileFlag = false; + uint outputAoIdx = 0; + uint outputIlIdx = 0; + + struct Settings + { + bool Enabled = true; + bool EnableGI = REL::Module::IsVR() ? false : true; // AO only for VR by default + bool EnableExperimentalSpecularGI = false; + bool EnableVanillaSSAO = false; + // performance/quality + uint NumSlices = REL::Module::IsVR() ? 3u : 4u; // AO preset for VR + uint NumSteps = REL::Module::IsVR() ? 6u : 8u; + int ResolutionMode = 1; // 0-full, 1-half, 2-quarter - DBF default + // visual + float MinScreenRadius = 0.01f; + float AORadius = 256.f; + float GIRadius = 256.f; + float Thickness = 32.f; + float2 DepthFadeRange = { 4e4, 5e4 }; + // gi + float GISaturation = 0.8f; + float GIDistanceCompensation = 0.f; + // mix + float AOPower = 1.0f; + float GIStrength = 1.0f; + // denoise + bool EnableTemporalDenoiser = true; + bool EnableBlur = true; + float DepthDisocclusion = .1f; + float NormalDisocclusion = .1f; + uint MaxAccumFrames = 16; + float BlurRadius = 2.f; + float DistanceNormalisation = 2.f; + } settings; + + struct alignas(16) SSGICB + { + float4x4 PrevInvViewMat[2]; + float2 NDCToViewMul[2]; + float2 NDCToViewAdd[2]; + + float2 TexDim; + float2 RcpTexDim; // + float2 FrameDim; + float2 RcpFrameDim; // + uint FrameIndex; + + uint NumSlices; + uint NumSteps; + + float MinScreenRadius; // + float AORadius; + float GIRadius; + float EffectRadius; + float Thickness; // + float2 DepthFadeRange; + float DepthFadeScaleConst; + + float GISaturation; // + float GIDistanceCompensation; + float GICompensationMaxDist; + float pad1; + + float AOPower; // + float GIStrength; + + float DepthDisocclusion; + float NormalDisocclusion; + uint MaxAccumFrames; // + + float BlurRadius; + float DistanceNormalisation; + + float2 pad; + }; + STATIC_ASSERT_ALIGNAS_16(SSGICB); + eastl::unique_ptr ssgiCB; + + eastl::unique_ptr texNoise = nullptr; + eastl::unique_ptr texWorkingDepth = nullptr; + winrt::com_ptr uavWorkingDepth[5] = { nullptr }; + eastl::unique_ptr texPrevGeo = nullptr; + eastl::unique_ptr texRadiance = nullptr; + eastl::unique_ptr texRadianceTemp = nullptr; + winrt::com_ptr uavRadiance[5] = { nullptr }; + eastl::unique_ptr texAccumFrames[2] = { nullptr }; + eastl::unique_ptr texAo[2] = { nullptr }; + eastl::unique_ptr texIlY[2] = { nullptr }; + eastl::unique_ptr texIlCoCg[2] = { nullptr }; + eastl::unique_ptr texGiSpecular[2] = { nullptr }; + + inline auto GetOutputTextures() + { + return (loaded && settings.Enabled) ? + std::make_tuple( + texAo[outputAoIdx]->srv.get(), + texIlY[outputIlIdx]->srv.get(), + texIlCoCg[outputIlIdx]->srv.get(), + texGiSpecular[outputAoIdx]->srv.get()) : + std::make_tuple(nullptr, nullptr, nullptr, nullptr); + } + + winrt::com_ptr linearClampSampler = nullptr; + winrt::com_ptr pointClampSampler = nullptr; + + winrt::com_ptr prefilterDepthsCompute = nullptr; + winrt::com_ptr prefilterRadianceCompute = nullptr; + winrt::com_ptr radianceDisoccCompute = nullptr; + winrt::com_ptr giCompute = nullptr; + winrt::com_ptr blurCompute = nullptr; + winrt::com_ptr stereoSyncCompute = nullptr; + winrt::com_ptr upsampleCompute = nullptr; +}; diff --git a/src/Features/ScreenSpaceShadows.h b/src/Features/ScreenSpaceShadows.h index e0ffd99599..c5490ad34f 100644 --- a/src/Features/ScreenSpaceShadows.h +++ b/src/Features/ScreenSpaceShadows.h @@ -1,109 +1,106 @@ -#pragma once - -#include "Buffer.h" - -struct ScreenSpaceShadows : Feature -{ -private: - static constexpr std::string_view MOD_ID = "93209"; - -public: - virtual inline std::string GetName() override { return "Screen Space Shadows"; } - virtual inline std::string GetShortName() override { return "ScreenSpaceShadows"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual inline std::string_view GetShaderDefineName() override { return "SCREEN_SPACE_SHADOWS"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } - - virtual std::pair> GetFeatureSummary() override - { - return { - "Screen Space Shadows enhances shadow quality by adding detailed contact shadows and improving shadow accuracy.\n" - "This technique adds fine-detail shadows that traditional shadow mapping might miss.", - { "Enhanced contact shadows", - "Improved shadow detail", - "Better shadow accuracy", - "Fine-scale shadow effects", - "Configurable shadow contrast" } - }; - } - - bool HasShaderDefine(RE::BSShader::Type shaderType) override; - - struct BendSettings - { - float SurfaceThickness = !globals::game::isVR ? 0.02f : 0.010f; - float BilinearThreshold = 0.02f; - float ShadowContrast = !globals::game::isVR ? 1.0f : 4.0f; - uint Enable = 1; - uint SampleCount = 1; - uint pad0[3]; - }; - - BendSettings bendSettings; - - struct alignas(16) RaymarchCB - { - // Runtime data returned from BuildDispatchList(): - float LightCoordinate[4]; // Values stored in DispatchList::LightCoordinate_Shader by BuildDispatchList() - int WaveOffset[2]; // Values stored in DispatchData::WaveOffset_Shader by BuildDispatchList() - - // Renderer Specific Values: - float FarDepthValue; // Set to the Depth Buffer Value for the far clip plane, as determined by renderer projection matrix setup (typically 0). - float NearDepthValue; // Set to the Depth Buffer Value for the near clip plane, as determined by renderer projection matrix setup (typically 1). - - // Sampling data: - float InvDepthTextureSize[2]; // Inverse of the texture dimensions for 'DepthTexture' (used to convert from pixel coordinates to UVs) - // If 'PointBorderSampler' is an Unnormalized sampler, then this value can be hard-coded to 1. - // The 'USE_HALF_PIXEL_OFFSET' macro might need to be defined if sampling at exact pixel coordinates isn't precise (e.g., if odd patterns appear in the shadow). - - float2 DynamicRes; - - BendSettings settings; - }; - STATIC_ASSERT_ALIGNAS_16(RaymarchCB); - - bool enableStereoSync = true; - - struct alignas(16) StereoSyncCB - { - float FrameDim[2]; - float RcpFrameDim[2]; - }; - STATIC_ASSERT_ALIGNAS_16(StereoSyncCB); - - ID3D11SamplerState* pointBorderSampler = nullptr; - - ConstantBuffer* raymarchCB = nullptr; - ID3D11ComputeShader* raymarchCS = nullptr; - ID3D11ComputeShader* raymarchRightCS = nullptr; - - Texture2D* screenSpaceShadowsTexture = nullptr; - - // VR stereo sync resources - Texture2D* stereoSyncCopyTex = nullptr; - ConstantBuffer* stereoSyncCB = nullptr; - ID3D11ComputeShader* stereoSyncCS = nullptr; - - virtual void SetupResources() override; - - virtual void DrawSettings() override; - - virtual void ClearShaderCache() override; - void InvalidateRaymarchShaders(); - uint GetScaledSampleCount(); - uint lastCompiledSampleCount = 0; - ID3D11ComputeShader* GetComputeRaymarch(); - ID3D11ComputeShader* GetComputeRaymarchRight(); - - virtual void Prepass() override; - - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - - void DrawShadows(); - void DrawStereoSync(); - - virtual void RestoreDefaultSettings() override; - - virtual bool SupportsVR() override { return true; }; -}; +#pragma once + +#include "Buffer.h" + +struct ScreenSpaceShadows : Feature +{ +private: +public: + virtual inline std::string GetName() override { return "Screen Space Shadows"; } + virtual inline std::string GetShortName() override { return "ScreenSpaceShadows"; } + virtual inline std::string_view GetShaderDefineName() override { return "SCREEN_SPACE_SHADOWS"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } + + virtual std::pair> GetFeatureSummary() override + { + return { + "Screen Space Shadows enhances shadow quality by adding detailed contact shadows and improving shadow accuracy.\n" + "This technique adds fine-detail shadows that traditional shadow mapping might miss.", + { "Enhanced contact shadows", + "Improved shadow detail", + "Better shadow accuracy", + "Fine-scale shadow effects", + "Configurable shadow contrast" } + }; + } + + bool HasShaderDefine(RE::BSShader::Type shaderType) override; + + struct BendSettings + { + float SurfaceThickness = !globals::game::isVR ? 0.02f : 0.010f; + float BilinearThreshold = 0.02f; + float ShadowContrast = !globals::game::isVR ? 1.0f : 4.0f; + uint Enable = 1; + uint SampleCount = 1; + uint pad0[3]; + }; + + BendSettings bendSettings; + + struct alignas(16) RaymarchCB + { + // Runtime data returned from BuildDispatchList(): + float LightCoordinate[4]; // Values stored in DispatchList::LightCoordinate_Shader by BuildDispatchList() + int WaveOffset[2]; // Values stored in DispatchData::WaveOffset_Shader by BuildDispatchList() + + // Renderer Specific Values: + float FarDepthValue; // Set to the Depth Buffer Value for the far clip plane, as determined by renderer projection matrix setup (typically 0). + float NearDepthValue; // Set to the Depth Buffer Value for the near clip plane, as determined by renderer projection matrix setup (typically 1). + + // Sampling data: + float InvDepthTextureSize[2]; // Inverse of the texture dimensions for 'DepthTexture' (used to convert from pixel coordinates to UVs) + // If 'PointBorderSampler' is an Unnormalized sampler, then this value can be hard-coded to 1. + // The 'USE_HALF_PIXEL_OFFSET' macro might need to be defined if sampling at exact pixel coordinates isn't precise (e.g., if odd patterns appear in the shadow). + + float2 DynamicRes; + + BendSettings settings; + }; + STATIC_ASSERT_ALIGNAS_16(RaymarchCB); + + bool enableStereoSync = true; + + struct alignas(16) StereoSyncCB + { + float FrameDim[2]; + float RcpFrameDim[2]; + }; + STATIC_ASSERT_ALIGNAS_16(StereoSyncCB); + + ID3D11SamplerState* pointBorderSampler = nullptr; + + ConstantBuffer* raymarchCB = nullptr; + ID3D11ComputeShader* raymarchCS = nullptr; + ID3D11ComputeShader* raymarchRightCS = nullptr; + + Texture2D* screenSpaceShadowsTexture = nullptr; + + // VR stereo sync resources + Texture2D* stereoSyncCopyTex = nullptr; + ConstantBuffer* stereoSyncCB = nullptr; + ID3D11ComputeShader* stereoSyncCS = nullptr; + + virtual void SetupResources() override; + + virtual void DrawSettings() override; + + virtual void ClearShaderCache() override; + void InvalidateRaymarchShaders(); + uint GetScaledSampleCount(); + uint lastCompiledSampleCount = 0; + ID3D11ComputeShader* GetComputeRaymarch(); + ID3D11ComputeShader* GetComputeRaymarchRight(); + + virtual void Prepass() override; + + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + void DrawShadows(); + void DrawStereoSync(); + + virtual void RestoreDefaultSettings() override; + + virtual bool SupportsVR() override { return true; }; +}; diff --git a/src/Features/SkySync.h b/src/Features/SkySync.h index af8af211ba..52aa2d93f4 100644 --- a/src/Features/SkySync.h +++ b/src/Features/SkySync.h @@ -1,205 +1,203 @@ -#pragma once -#include "RE/M/Moon.h" - -struct SkySync : Feature -{ -private: - static constexpr std::string_view MOD_ID = "153543"; - -public: - virtual inline std::string GetName() override { return "Sky Sync"; } - virtual inline std::string GetShortName() override { return "SkySync"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kSky; } - - virtual std::pair> GetFeatureSummary() override - { - return { - "Synchronizes volumetric lighting and shadows with the actual sun and moon positions in the sky.", - { "Fixes the mismatch between the positions of the sun and moons and the lighting direction", - "Includes a configurable alternative sun path for more realistic and dramatic lighting", - "Smoothly switches the light source between the sun and moons based on visibility", - "Moon light source can be switched between Masser, Secunda, or the brightest", - "Automatic calculation of moon lighting intensity based on moon phase", - "Fixes the sun appearing higher on the horizon when the player gains altitude" } - }; - } - - struct Settings - { - bool Enabled = true; - bool UseAlternateSunPath = true; - int32_t MoonLightSource = 0; - int32_t SunPath = 0; - float CustomAngle = -35.0f; - float SunriseBeginOffset = 0.0f; - float SunriseEndOffset = 0.0f; - float SunsetBeginOffset = 0.0f; - float SunsetEndOffset = 0.0f; - float MinShadowElevation = 0.25f; - }; - - Settings settings; - - virtual void DrawSettings() override; - - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - virtual void RestoreDefaultSettings() override; - - virtual bool SupportsVR() override { return true; } - - virtual void PostPostLoad() override; - virtual void DataLoaded() override; - - struct Sky_Update - { - static void thunk(RE::Sky* sky); - static inline REL::Relocation func; - }; - - struct Sky_OnNewClimate - { - static void thunk(RE::Sky* sky); - static inline REL::Relocation func; - }; - - struct Moon_Update - { - static void thunk(RE::Moon* moon, RE::Sky* sky); - static inline REL::Relocation func; - }; - -private: - enum class CellFlagExt : uint16_t - { - kSunlightShadows = 1 << 15, - }; - - enum class MoonLightSource : uint8_t - { - Brightest, - Masser, - Secunda, - Count - }; - - enum class Caster : uint8_t - { - Sun, - Masser, - Secunda, - None - }; - - enum class SunPath : uint8_t - { - Southern, - Northern, - Vanilla, - Custom, - Count - }; - - const char* MoonLightSourceNames[static_cast(MoonLightSource::Count)] = { "Brightest", "Masser", "Secunda" }; - const char* SunPathNames[static_cast(SunPath::Count)] = { "Southern Sky", "Northern Sky", "Vanilla", "Custom" }; - - struct ClimateTimings - { - float sunriseFadeOutMoonStart; - float sunriseBegin; - float sunriseFadeOutMoonEnd; - float sunrise; - float sunriseEnd; - float sunsetBegin; - float sunset; - float sunsetFadeInMoonStart; - float sunsetEnd; - float sunsetFadeInMoonEnd; - - void Update(const RE::TESClimate* climate); - }; - - struct ShadowFader - { - enum class Phase : uint8_t - { - None, - FadeOut, - FadeIn - }; - - static constexpr float FadeTime = 100.0f; // 5 seconds at timescale 20 - - Phase fadePhase = Phase::None; - Caster current = Caster::None; - Caster target = Caster::None; - float fadeTimer = 0.0f; - float previousHoursPassed = 0.0f; - - void Update(const RE::Sun* sun, RE::NiPoint3 dirs[], float intensities[], bool isDayTime); - static void SetLighting(const RE::Sun* sun, RE::NiPoint3 dir, float intensity); - static void ClampDirection(RE::NiPoint3& dir); - void Reset(); - }; - - static constexpr float RenderDistance = 325000.0f; - static constexpr float SunHorizonDistance = 280.0f; - static constexpr float SunPeakDistance = 400.0f; - static constexpr float SunScaleFactor = 48.0f / 2048.0f; - - static constexpr float SouthernSunAngle = 90.0f - 35.0f; - static constexpr float NorthernSunAngle = 90.0f + 35.0f; - static constexpr float VanillaSunAngle = 90.0f + 5.0f; - - static constexpr float SecundaIntensityFactor = 0.67f; - static constexpr float NewMoonIntensityFactor = 0.05f; - static constexpr float CrescentMoonIntensityFactor = 0.25f; - static constexpr float FullMoonIntensityFactor = 1.0f; - - inline static RE::NiPoint3* gSunPosition = nullptr; - inline static float* gSunGlareSize = nullptr; - inline static uint32_t* gMasserSize = nullptr; - inline static uint32_t* gSecundaSize = nullptr; - - bool moonAndStarsLoaded = false; - RE::TESObjectCELL* currentCell = nullptr; - float sunAngle = 90.0f; - float currentSkyRotation = D3D11_FLOAT32_MAX; - float masserPhaseIntensityFactor = 0.0f; - float secundaPhaseIntensityFactor = 0.0f; - - ClimateTimings timings = {}; - - RE::NiPoint3 rawDirections[3]; - RE::NiPoint3 directions[3]; - float intensities[3] = {}; - ShadowFader shadowFader; - - void DisableOnConflict(std::string_view conflictName); - - void Update(const RE::Sky* sky); - - void SetSunAngle(); - - void SetSkyRotation(const RE::Sky* sky, RE::TESObjectCELL* cell); - - void ProcessSun(const RE::Sun* sun, float time, float altitude, bool isDayTime); - - void ProcessMoon(const RE::Moon* moon, float time, Caster type, float altitude, bool isDayTime); - - static void CalculateSunDirectionAndDistance(const RE::Sun* sun, RE::NiPoint3& outDir, float& outDistance); - - static void CalculateAlternateSunDirectionAndDistance(RE::NiPoint3& outDir, float& outDist, float time, float sunrise, float sunset, float sunAngle); - - static RE::NiPoint3 GetApparentDirection(const RE::NiPoint3& dir, float altitude); - - static void SetSunPosition(const RE::Sun* sun, const RE::NiPoint3& dir, float distance); - - static void SetMoonDirection(const RE::Moon* moon, const RE::NiPoint3& dir); - - static float CalculateVisibility(const RE::NiPoint3& dir, float dist, float radius); - - static void SetSunBaseVisibility(const RE::Sun* sun, float visibility); - - static float SmoothStep(float start, float end, float x); -}; +#pragma once +#include "RE/M/Moon.h" + +struct SkySync : Feature +{ +private: +public: + virtual inline std::string GetName() override { return "Sky Sync"; } + virtual inline std::string GetShortName() override { return "SkySync"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kSky; } + + virtual std::pair> GetFeatureSummary() override + { + return { + "Synchronizes volumetric lighting and shadows with the actual sun and moon positions in the sky.", + { "Fixes the mismatch between the positions of the sun and moons and the lighting direction", + "Includes a configurable alternative sun path for more realistic and dramatic lighting", + "Smoothly switches the light source between the sun and moons based on visibility", + "Moon light source can be switched between Masser, Secunda, or the brightest", + "Automatic calculation of moon lighting intensity based on moon phase", + "Fixes the sun appearing higher on the horizon when the player gains altitude" } + }; + } + + struct Settings + { + bool Enabled = true; + bool UseAlternateSunPath = true; + int32_t MoonLightSource = 0; + int32_t SunPath = 0; + float CustomAngle = -35.0f; + float SunriseBeginOffset = 0.0f; + float SunriseEndOffset = 0.0f; + float SunsetBeginOffset = 0.0f; + float SunsetEndOffset = 0.0f; + float MinShadowElevation = 0.25f; + }; + + Settings settings; + + virtual void DrawSettings() override; + + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + virtual void RestoreDefaultSettings() override; + + virtual bool SupportsVR() override { return true; } + + virtual void PostPostLoad() override; + virtual void DataLoaded() override; + + struct Sky_Update + { + static void thunk(RE::Sky* sky); + static inline REL::Relocation func; + }; + + struct Sky_OnNewClimate + { + static void thunk(RE::Sky* sky); + static inline REL::Relocation func; + }; + + struct Moon_Update + { + static void thunk(RE::Moon* moon, RE::Sky* sky); + static inline REL::Relocation func; + }; + +private: + enum class CellFlagExt : uint16_t + { + kSunlightShadows = 1 << 15, + }; + + enum class MoonLightSource : uint8_t + { + Brightest, + Masser, + Secunda, + Count + }; + + enum class Caster : uint8_t + { + Sun, + Masser, + Secunda, + None + }; + + enum class SunPath : uint8_t + { + Southern, + Northern, + Vanilla, + Custom, + Count + }; + + const char* MoonLightSourceNames[static_cast(MoonLightSource::Count)] = { "Brightest", "Masser", "Secunda" }; + const char* SunPathNames[static_cast(SunPath::Count)] = { "Southern Sky", "Northern Sky", "Vanilla", "Custom" }; + + struct ClimateTimings + { + float sunriseFadeOutMoonStart; + float sunriseBegin; + float sunriseFadeOutMoonEnd; + float sunrise; + float sunriseEnd; + float sunsetBegin; + float sunset; + float sunsetFadeInMoonStart; + float sunsetEnd; + float sunsetFadeInMoonEnd; + + void Update(const RE::TESClimate* climate); + }; + + struct ShadowFader + { + enum class Phase : uint8_t + { + None, + FadeOut, + FadeIn + }; + + static constexpr float FadeTime = 100.0f; // 5 seconds at timescale 20 + + Phase fadePhase = Phase::None; + Caster current = Caster::None; + Caster target = Caster::None; + float fadeTimer = 0.0f; + float previousHoursPassed = 0.0f; + + void Update(const RE::Sun* sun, RE::NiPoint3 dirs[], float intensities[], bool isDayTime); + static void SetLighting(const RE::Sun* sun, RE::NiPoint3 dir, float intensity); + static void ClampDirection(RE::NiPoint3& dir); + void Reset(); + }; + + static constexpr float RenderDistance = 325000.0f; + static constexpr float SunHorizonDistance = 280.0f; + static constexpr float SunPeakDistance = 400.0f; + static constexpr float SunScaleFactor = 48.0f / 2048.0f; + + static constexpr float SouthernSunAngle = 90.0f - 35.0f; + static constexpr float NorthernSunAngle = 90.0f + 35.0f; + static constexpr float VanillaSunAngle = 90.0f + 5.0f; + + static constexpr float SecundaIntensityFactor = 0.67f; + static constexpr float NewMoonIntensityFactor = 0.05f; + static constexpr float CrescentMoonIntensityFactor = 0.25f; + static constexpr float FullMoonIntensityFactor = 1.0f; + + inline static RE::NiPoint3* gSunPosition = nullptr; + inline static float* gSunGlareSize = nullptr; + inline static uint32_t* gMasserSize = nullptr; + inline static uint32_t* gSecundaSize = nullptr; + + bool moonAndStarsLoaded = false; + RE::TESObjectCELL* currentCell = nullptr; + float sunAngle = 90.0f; + float currentSkyRotation = D3D11_FLOAT32_MAX; + float masserPhaseIntensityFactor = 0.0f; + float secundaPhaseIntensityFactor = 0.0f; + + ClimateTimings timings = {}; + + RE::NiPoint3 rawDirections[3]; + RE::NiPoint3 directions[3]; + float intensities[3] = {}; + ShadowFader shadowFader; + + void DisableOnConflict(std::string_view conflictName); + + void Update(const RE::Sky* sky); + + void SetSunAngle(); + + void SetSkyRotation(const RE::Sky* sky, RE::TESObjectCELL* cell); + + void ProcessSun(const RE::Sun* sun, float time, float altitude, bool isDayTime); + + void ProcessMoon(const RE::Moon* moon, float time, Caster type, float altitude, bool isDayTime); + + static void CalculateSunDirectionAndDistance(const RE::Sun* sun, RE::NiPoint3& outDir, float& outDistance); + + static void CalculateAlternateSunDirectionAndDistance(RE::NiPoint3& outDir, float& outDist, float time, float sunrise, float sunset, float sunAngle); + + static RE::NiPoint3 GetApparentDirection(const RE::NiPoint3& dir, float altitude); + + static void SetSunPosition(const RE::Sun* sun, const RE::NiPoint3& dir, float distance); + + static void SetMoonDirection(const RE::Moon* moon, const RE::NiPoint3& dir); + + static float CalculateVisibility(const RE::NiPoint3& dir, float dist, float radius); + + static void SetSunBaseVisibility(const RE::Sun* sun, float visibility); + + static float SmoothStep(float start, float end, float x); +}; diff --git a/src/Features/Skylighting.h b/src/Features/Skylighting.h index b1caa35292..2fe720e7ee 100644 --- a/src/Features/Skylighting.h +++ b/src/Features/Skylighting.h @@ -1,147 +1,144 @@ -#pragma once - -struct Skylighting : Feature -{ -private: - static constexpr std::string_view MOD_ID = "139352"; - -public: - virtual bool SupportsVR() override { return true; }; - - virtual inline std::string GetName() override { return "Skylighting"; } - virtual inline std::string GetShortName() override { return "Skylighting"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual inline std::string_view GetShaderDefineName() override { return "SKYLIGHTING"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } - virtual std::pair> GetFeatureSummary() override - { - return { - "Simulates realistic ambient lighting by calculating sky occlusion and directional lighting, providing more accurate and natural illumination in outdoor environments.", - { "Sky occlusion calculation for ambient lighting", - "Directional skylighting based on environment geometry", - "Enhanced ambient lighting for outdoor scenes", - "Support for varying sky illumination intensities", - "Integration with existing lighting systems" } - }; - } - virtual bool HasShaderDefine(RE::BSShader::Type) override { return true; }; - - virtual void RestoreDefaultSettings() override; - virtual void DrawSettings() override; - - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - - virtual void SetupResources() override; - virtual void ClearShaderCache() override; - void CompileComputeShaders(); - - virtual void Prepass() override; - - virtual void PostPostLoad() override; - - ////////////////////////////////////////////////////////////////////////////////// - - struct Settings - { - float MaxZenith = 3.1415926f / 2.f; // 90 deg - float MinDiffuseVisibility = 0.1f; - float MinSpecularVisibility = 0.1f; - } settings; - - struct SkylightingCB - { - REX::W32::XMFLOAT4X4 OcclusionViewProj; - float4 OcclusionDir; - - float3 PosOffset; // cell origin in camera model space - uint _pad0; - uint ArrayOrigin[3]; // xyz: array origin, w: max accum frames - uint _pad1; - int ValidMargin[4]; - - float MinDiffuseVisibility; - float MinSpecularVisibility; - uint _pad2[2]; - }; - static_assert(sizeof(SkylightingCB) % 16 == 0); - - SkylightingCB GetCommonBufferData(bool a_inWorld); - - winrt::com_ptr comparisonSampler = nullptr; - - Texture2D* texOcclusion = nullptr; - Texture3D* texProbeArray = nullptr; - Texture3D* texAccumFramesArray = nullptr; - - winrt::com_ptr probeUpdateCompute = nullptr; - winrt::com_ptr stbn_vec3_2Dx1D_128x128x64; - - // misc parameters - uint probeArrayDims[3] = { 256, 256, 128 }; - float occlusionDistance = 4096.f * 2.5f; // 5 ugrids - - // cached variables - bool queuedResetSkylighting = true; - bool inOcclusion = false; - REX::W32::XMFLOAT4X4 OcclusionTransform; - float4 OcclusionDir; - uint frameCount = 0; - - void ResetSkylighting(); - - std::chrono::time_point lastUpdateTimer = std::chrono::system_clock::now(); - - ////////////////////////////////////////////////////////////////////////////////// - - // Hooks - struct BSLightingShaderProperty_GetPrecipitationOcclusionMapRenderPassesImpl - { - static RE::BSShaderProperty::RenderPassArray* thunk(RE::BSLightingShaderProperty* property, RE::BSGeometry* geometry, uint32_t renderMode, RE::BSGraphics::BSShaderAccumulator* accumulator); - static inline REL::Relocation func; - }; - - void RenderOcclusion(); - - struct Main_Precipitation_RenderOcclusion - { - static void thunk(); - static inline REL::Relocation func; - }; - - struct SetViewFrustum - { - static void thunk(RE::NiCamera* a_camera, RE::NiFrustum* a_frustum); - static inline REL::Relocation func; - }; - - struct SetViewFrustumVR - { - static void thunk(RE::NiCamera* a_camera, RE::NiFrustum* a_frustum, uint a_eyeIndex); - static inline REL::Relocation func; - }; - - // Event handler - class MenuOpenCloseEventHandler : public RE::BSTEventSink - { - public: - virtual RE::BSEventNotifyControl ProcessEvent(const RE::MenuOpenCloseEvent* a_event, RE::BSTEventSource*); - - static bool Register() - { - static MenuOpenCloseEventHandler singleton; - auto ui = globals::game::ui; - - if (!ui) { - logger::error("UI event source not found"); - return false; - } - - ui->GetEventSource()->AddEventSink(&singleton); - - logger::info("Registered {}", typeid(singleton).name()); - - return true; - } - }; -}; +#pragma once + +struct Skylighting : Feature +{ +private: +public: + virtual bool SupportsVR() override { return true; }; + + virtual inline std::string GetName() override { return "Skylighting"; } + virtual inline std::string GetShortName() override { return "Skylighting"; } + virtual inline std::string_view GetShaderDefineName() override { return "SKYLIGHTING"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kLighting; } + virtual std::pair> GetFeatureSummary() override + { + return { + "Simulates realistic ambient lighting by calculating sky occlusion and directional lighting, providing more accurate and natural illumination in outdoor environments.", + { "Sky occlusion calculation for ambient lighting", + "Directional skylighting based on environment geometry", + "Enhanced ambient lighting for outdoor scenes", + "Support for varying sky illumination intensities", + "Integration with existing lighting systems" } + }; + } + virtual bool HasShaderDefine(RE::BSShader::Type) override { return true; }; + + virtual void RestoreDefaultSettings() override; + virtual void DrawSettings() override; + + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + virtual void SetupResources() override; + virtual void ClearShaderCache() override; + void CompileComputeShaders(); + + virtual void Prepass() override; + + virtual void PostPostLoad() override; + + ////////////////////////////////////////////////////////////////////////////////// + + struct Settings + { + float MaxZenith = 3.1415926f / 2.f; // 90 deg + float MinDiffuseVisibility = 0.1f; + float MinSpecularVisibility = 0.1f; + } settings; + + struct SkylightingCB + { + REX::W32::XMFLOAT4X4 OcclusionViewProj; + float4 OcclusionDir; + + float3 PosOffset; // cell origin in camera model space + uint _pad0; + uint ArrayOrigin[3]; // xyz: array origin, w: max accum frames + uint _pad1; + int ValidMargin[4]; + + float MinDiffuseVisibility; + float MinSpecularVisibility; + uint _pad2[2]; + }; + static_assert(sizeof(SkylightingCB) % 16 == 0); + + SkylightingCB GetCommonBufferData(bool a_inWorld); + + winrt::com_ptr comparisonSampler = nullptr; + + Texture2D* texOcclusion = nullptr; + Texture3D* texProbeArray = nullptr; + Texture3D* texAccumFramesArray = nullptr; + + winrt::com_ptr probeUpdateCompute = nullptr; + winrt::com_ptr stbn_vec3_2Dx1D_128x128x64; + + // misc parameters + uint probeArrayDims[3] = { 256, 256, 128 }; + float occlusionDistance = 4096.f * 2.5f; // 5 ugrids + + // cached variables + bool queuedResetSkylighting = true; + bool inOcclusion = false; + REX::W32::XMFLOAT4X4 OcclusionTransform; + float4 OcclusionDir; + uint frameCount = 0; + + void ResetSkylighting(); + + std::chrono::time_point lastUpdateTimer = std::chrono::system_clock::now(); + + ////////////////////////////////////////////////////////////////////////////////// + + // Hooks + struct BSLightingShaderProperty_GetPrecipitationOcclusionMapRenderPassesImpl + { + static RE::BSShaderProperty::RenderPassArray* thunk(RE::BSLightingShaderProperty* property, RE::BSGeometry* geometry, uint32_t renderMode, RE::BSGraphics::BSShaderAccumulator* accumulator); + static inline REL::Relocation func; + }; + + void RenderOcclusion(); + + struct Main_Precipitation_RenderOcclusion + { + static void thunk(); + static inline REL::Relocation func; + }; + + struct SetViewFrustum + { + static void thunk(RE::NiCamera* a_camera, RE::NiFrustum* a_frustum); + static inline REL::Relocation func; + }; + + struct SetViewFrustumVR + { + static void thunk(RE::NiCamera* a_camera, RE::NiFrustum* a_frustum, uint a_eyeIndex); + static inline REL::Relocation func; + }; + + // Event handler + class MenuOpenCloseEventHandler : public RE::BSTEventSink + { + public: + virtual RE::BSEventNotifyControl ProcessEvent(const RE::MenuOpenCloseEvent* a_event, RE::BSTEventSource*); + + static bool Register() + { + static MenuOpenCloseEventHandler singleton; + auto ui = globals::game::ui; + + if (!ui) { + logger::error("UI event source not found"); + return false; + } + + ui->GetEventSource()->AddEventSink(&singleton); + + logger::info("Registered {}", typeid(singleton).name()); + + return true; + } + }; +}; diff --git a/src/Features/SubsurfaceScattering.h b/src/Features/SubsurfaceScattering.h index b9e6cdc321..6ad576435b 100644 --- a/src/Features/SubsurfaceScattering.h +++ b/src/Features/SubsurfaceScattering.h @@ -1,133 +1,130 @@ -#pragma once - -#include "Buffer.h" - -#define SSSS_N_SAMPLES 21 - -struct SubsurfaceScattering : Feature -{ -private: - static constexpr std::string_view MOD_ID = "114114"; - -public: - struct DiffusionProfile - { - float BlurRadius; - float Thickness; - float3 Strength; - float3 Falloff; - }; - - struct Settings - { - uint EnableCharacterLighting = false; - float CharacterLightingStrength = 1.0f; - int SSMode = 1; - DiffusionProfile BaseProfile{ 0.5f, 1.0f, { 0.48f, 0.41f, 0.28f }, { 0.56f, 0.56f, 0.56f } }; - DiffusionProfile HumanProfile{ 0.5f, 1.0f, { 0.48f, 0.41f, 0.28f }, { 1.0f, 0.37f, 0.3f } }; - uint BurleySamples = 16; - float4 MeanFreePathBase = { 0.56f, 0.56f, 0.56f, 2.67f }; - float4 MeanFreePathHuman = { 1.0f, 0.37f, 0.3f, 2.67f }; - }; - - Settings settings; - - float CharacterLightingStrengthOriginal = -1.0f; - - struct alignas(16) Kernel - { - float4 Sample[SSSS_N_SAMPLES]; - }; - STATIC_ASSERT_ALIGNAS_16(Kernel); - - struct alignas(16) BlurCB - { - Kernel BaseKernel; - Kernel HumanKernel; - float4 BaseProfile; - float4 HumanProfile; - float SSSS_FOVY; - uint BurleySamples; - uint pad[2]; - float4 MeanFreePathBase; - float4 MeanFreePathHuman; - }; - STATIC_ASSERT_ALIGNAS_16(BlurCB); - - ConstantBuffer* blurCB = nullptr; - BlurCB blurCBData{}; - - bool validMaterial = true; - bool updateKernels = true; - bool validMaterials = false; - - Texture2D* blurHorizontalTemp = nullptr; - - ID3D11ComputeShader* horizontalSSBlur = nullptr; - ID3D11ComputeShader* verticalSSBlur = nullptr; - ID3D11ComputeShader* burleySS = nullptr; - RE::BGSKeyword* isBeastRaceKeyword = nullptr; - - virtual inline std::string GetName() override { return "Subsurface Scattering"; } - virtual inline std::string GetShortName() override { return "SubsurfaceScattering"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual inline std::string_view GetShaderDefineName() override { return "SSS"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kCharacters; } - - virtual std::pair> GetFeatureSummary() override - { - return { - "Subsurface Scattering simulates light penetration through translucent materials like skin, creating more realistic character lighting.\n" - "This technique makes organic materials appear more lifelike and natural.", - { "Realistic skin lighting", - "Light penetration simulation", - "Separate profiles for different materials", - "Enhanced character appearance", - "Configurable scattering properties" } - }; - } - - bool HasShaderDefine(RE::BSShader::Type) override { return true; }; - - virtual void SetupResources() override; - virtual void Reset() override; - virtual void RestoreDefaultSettings() override; - - virtual void DrawSettings() override; - - float3 Gaussian(DiffusionProfile& a_profile, float variance, float r); - float3 Profile(DiffusionProfile& a_profile, float r); - void CalculateKernel(DiffusionProfile& a_profile, Kernel& kernel); - - void DrawSSS(); - - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - - virtual void ClearShaderCache() override; - ID3D11ComputeShader* GetComputeShaderHorizontalBlur(); - ID3D11ComputeShader* GetComputeShaderVerticalBlur(); - ID3D11ComputeShader* GetComputeShaderBurley(); - - virtual void DataLoaded() override; - virtual void PostPostLoad() override; - - void BSLightingShader_SetupSkin(RE::BSRenderPass* Pass); - - struct Hooks - { - struct BSLightingShader_SetupGeometry - { - static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); - static inline REL::Relocation func; - }; - - static void Install() - { - stl::write_vfunc<0x6, BSLightingShader_SetupGeometry>(RE::VTABLE_BSLightingShader[0]); - logger::info("[SSS] Installed hooks"); - } - }; - - virtual bool SupportsVR() override { return true; }; -}; +#pragma once + +#include "Buffer.h" + +#define SSSS_N_SAMPLES 21 + +struct SubsurfaceScattering : Feature +{ +private: +public: + struct DiffusionProfile + { + float BlurRadius; + float Thickness; + float3 Strength; + float3 Falloff; + }; + + struct Settings + { + uint EnableCharacterLighting = false; + float CharacterLightingStrength = 1.0f; + int SSMode = 1; + DiffusionProfile BaseProfile{ 0.5f, 1.0f, { 0.48f, 0.41f, 0.28f }, { 0.56f, 0.56f, 0.56f } }; + DiffusionProfile HumanProfile{ 0.5f, 1.0f, { 0.48f, 0.41f, 0.28f }, { 1.0f, 0.37f, 0.3f } }; + uint BurleySamples = 16; + float4 MeanFreePathBase = { 0.56f, 0.56f, 0.56f, 2.67f }; + float4 MeanFreePathHuman = { 1.0f, 0.37f, 0.3f, 2.67f }; + }; + + Settings settings; + + float CharacterLightingStrengthOriginal = -1.0f; + + struct alignas(16) Kernel + { + float4 Sample[SSSS_N_SAMPLES]; + }; + STATIC_ASSERT_ALIGNAS_16(Kernel); + + struct alignas(16) BlurCB + { + Kernel BaseKernel; + Kernel HumanKernel; + float4 BaseProfile; + float4 HumanProfile; + float SSSS_FOVY; + uint BurleySamples; + uint pad[2]; + float4 MeanFreePathBase; + float4 MeanFreePathHuman; + }; + STATIC_ASSERT_ALIGNAS_16(BlurCB); + + ConstantBuffer* blurCB = nullptr; + BlurCB blurCBData{}; + + bool validMaterial = true; + bool updateKernels = true; + bool validMaterials = false; + + Texture2D* blurHorizontalTemp = nullptr; + + ID3D11ComputeShader* horizontalSSBlur = nullptr; + ID3D11ComputeShader* verticalSSBlur = nullptr; + ID3D11ComputeShader* burleySS = nullptr; + RE::BGSKeyword* isBeastRaceKeyword = nullptr; + + virtual inline std::string GetName() override { return "Subsurface Scattering"; } + virtual inline std::string GetShortName() override { return "SubsurfaceScattering"; } + virtual inline std::string_view GetShaderDefineName() override { return "SSS"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kCharacters; } + + virtual std::pair> GetFeatureSummary() override + { + return { + "Subsurface Scattering simulates light penetration through translucent materials like skin, creating more realistic character lighting.\n" + "This technique makes organic materials appear more lifelike and natural.", + { "Realistic skin lighting", + "Light penetration simulation", + "Separate profiles for different materials", + "Enhanced character appearance", + "Configurable scattering properties" } + }; + } + + bool HasShaderDefine(RE::BSShader::Type) override { return true; }; + + virtual void SetupResources() override; + virtual void Reset() override; + virtual void RestoreDefaultSettings() override; + + virtual void DrawSettings() override; + + float3 Gaussian(DiffusionProfile& a_profile, float variance, float r); + float3 Profile(DiffusionProfile& a_profile, float r); + void CalculateKernel(DiffusionProfile& a_profile, Kernel& kernel); + + void DrawSSS(); + + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + virtual void ClearShaderCache() override; + ID3D11ComputeShader* GetComputeShaderHorizontalBlur(); + ID3D11ComputeShader* GetComputeShaderVerticalBlur(); + ID3D11ComputeShader* GetComputeShaderBurley(); + + virtual void DataLoaded() override; + virtual void PostPostLoad() override; + + void BSLightingShader_SetupSkin(RE::BSRenderPass* Pass); + + struct Hooks + { + struct BSLightingShader_SetupGeometry + { + static void thunk(RE::BSShader* This, RE::BSRenderPass* Pass, uint32_t RenderFlags); + static inline REL::Relocation func; + }; + + static void Install() + { + stl::write_vfunc<0x6, BSLightingShader_SetupGeometry>(RE::VTABLE_BSLightingShader[0]); + logger::info("[SSS] Installed hooks"); + } + }; + + virtual bool SupportsVR() override { return true; }; +}; diff --git a/src/Features/TerrainBlending.h b/src/Features/TerrainBlending.h index 4d552b61c8..c3a867b46d 100644 --- a/src/Features/TerrainBlending.h +++ b/src/Features/TerrainBlending.h @@ -1,170 +1,170 @@ -#pragma once - -struct TerrainBlending : Feature -{ -public: - virtual inline std::string GetName() override { return "Terrain Blending"; } - virtual inline std::string GetShortName() override { return "TerrainBlending"; } - virtual inline std::string_view GetShaderDefineName() override { return "TERRAIN_BLENDING"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kLandscapeAndTextures; } - virtual std::pair> GetFeatureSummary() override - { - return { - "Provides seamless blending between terrain and objects, eliminating harsh transitions where objects meet the ground for more natural-looking landscapes.", - { "Seamless terrain-to-object blending transitions", - "Advanced depth buffer manipulation for smooth integration", - "Support for alternative terrain rendering modes", - "Multi-pass rendering optimization for complex scenes", - "Enhanced visual continuity in landscape interactions" } - }; - } - virtual inline bool HasShaderDefine(RE::BSShader::Type) override { return true; } - virtual bool SupportsVR() override { return true; } - - struct Settings - { - uint32_t Enabled = true; - uint32_t pad[3]; - }; - STATIC_ASSERT_ALIGNAS_16(Settings); - - Settings settings; - - virtual void DrawSettings() override; - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - - virtual void SetupResources() override; - - ID3D11VertexShader* GetTerrainVertexShader(); - ID3D11VertexShader* GetTerrainOffsetVertexShader(); - - ID3D11VertexShader* terrainVertexShader = nullptr; - ID3D11VertexShader* terrainOffsetVertexShader = nullptr; - - ID3D11ComputeShader* GetDepthBlendShader(); - - virtual void PostPostLoad() override; - virtual void DataLoaded() override; - - bool renderDepth = false; - bool renderTerrainDepth = false; - bool renderAltTerrain = false; - - RE::NiPoint3 averageEyePosition; - - struct RenderPass - { - RE::BSRenderPass* a_pass; - uint32_t a_technique; - bool a_alphaTest; - uint32_t a_renderFlags; - }; - - std::vector renderPasses; - std::vector terrainRenderPasses; - - void TerrainShaderHacks(); - - void ResetDepth(); - void ResetTerrainDepth(); - void BlendPrepassDepths(); - - Texture2D* blendedDepthTexture = nullptr; - Texture2D* blendedDepthTexture16 = nullptr; - - ID3D11ShaderResourceView* GetBlendedDepthSRV() const - { - if (blendedDepthTexture && blendedDepthTexture->srv) - return blendedDepthTexture->srv.get(); - return nullptr; - } - - RE::BSGraphics::DepthStencilData terrainDepth; - - ID3D11DepthStencilState* terrainDepthStencilState = nullptr; - - ID3D11ShaderResourceView* depthSRVBackup = nullptr; - ID3D11ShaderResourceView* prepassSRVBackup = nullptr; - - ID3D11ComputeShader* depthBlendShader = nullptr; - - virtual void ClearShaderCache() override; - - void RenderTerrainBlendingPasses(); - void OnBeginTechnique(RE::BSShader* a_shader, uint32_t a_pixelDescriptor, uint32_t a_callerRva = 0); - void OnShadowmaskPhaseEnd(); - void OnUtilitySetupGeometry(RE::BSShader* a_shader, RE::BSRenderPass* a_pass, uint32_t a_renderFlags, uint32_t a_callerRva = 0); - void OnShaderPropertySetupGeometry(RE::BSShaderProperty* a_shaderProperty, RE::BSGeometry* a_geometry, bool a_result, uint32_t a_callerRva = 0); - void OnSetDirtyStates(bool a_isCompute, uint32_t a_callerRva = 0); - - struct Hooks - { - struct Main_RenderDepth - { - static void thunk(bool a1, bool a2); - static inline REL::Relocation func; - }; - - struct Main_RenderShadowmasks - { - static void thunk(bool a1); - static inline REL::Relocation func; - }; - - struct BSBatchRenderer__RenderPassImmediately - { - static void thunk(RE::BSRenderPass* a_pass, uint32_t a_technique, bool a_alphaTest, uint32_t a_renderFlags); - static inline REL::Relocation func; - }; - - struct BSUtilityShader_SetupGeometry - { - static void thunk(RE::BSShader* a_shader, RE::BSRenderPass* a_pass, uint32_t a_renderFlags); - static inline REL::Relocation func; - }; - - struct BSShaderProperty_SetupGeometry - { - static bool thunk(RE::BSShaderProperty* a_shaderProperty, RE::BSGeometry* a_geometry); - static inline REL::Relocation func; - }; - - struct BSShader_BeginTechnique - { - static bool thunk(RE::BSShader* shader, uint32_t vertexDescriptor, uint32_t pixelDescriptor, bool skipPixelShader); - static inline REL::Relocation func; - }; - - struct BSGraphics_SetDirtyStates - { - static void thunk(bool isCompute); - static inline REL::Relocation func; - }; - - static void Install() - { - // To know when we are rendering z-prepass depth vs shadows depth - stl::write_thunk_call(REL::RelocationID(35560, 36559).address() + REL::Relocate(0x395, 0x395, 0x2EE)); - - // To know when shadowmask phase ends (for releasing engine hook overrides) - stl::detour_thunk(REL::RelocationID(100422, 107140)); - - // To manipulate the depth buffer write, depth testing, alpha blending - stl::write_thunk_call(REL::RelocationID(100852, 107642).address() + REL::Relocate(0x29E, 0x28F)); - - // Engine path: late Utility setup hook so slot rebinding survives to draw. - stl::write_vfunc<0x6, BSUtilityShader_SetupGeometry>(RE::VTABLE_BSUtilityShader[0]); - - // Engine path: even later material/property setup hook for final slot correction. - stl::write_vfunc<0x27, BSShaderProperty_SetupGeometry>(RE::VTABLE_BSShaderProperty[0]); - - // Chained on top of Hooks.cpp's detours to intercept BeginTechnique/SetDirtyStates - // for engine SRV slot override during the shadowmask phase. - stl::detour_thunk(REL::RelocationID(101341, 108328)); - stl::detour_thunk(REL::RelocationID(75580, 77386)); - - logger::info("[Terrain Blending] Installed hooks"); - } - }; -}; +#pragma once + +struct TerrainBlending : Feature +{ +public: + virtual inline std::string GetName() override { return "Terrain Blending"; } + virtual inline std::string GetShortName() override { return "TerrainBlending"; } + virtual inline std::string_view GetShaderDefineName() override { return "TERRAIN_BLENDING"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kLandscapeAndTextures; } + virtual std::pair> GetFeatureSummary() override + { + return { + "Provides seamless blending between terrain and objects, eliminating harsh transitions where objects meet the ground for more natural-looking landscapes.", + { "Seamless terrain-to-object blending transitions", + "Advanced depth buffer manipulation for smooth integration", + "Support for alternative terrain rendering modes", + "Multi-pass rendering optimization for complex scenes", + "Enhanced visual continuity in landscape interactions" } + }; + } + virtual inline bool HasShaderDefine(RE::BSShader::Type) override { return true; } + virtual bool SupportsVR() override { return true; } + + struct Settings + { + uint32_t Enabled = true; + uint32_t pad[3]; + }; + STATIC_ASSERT_ALIGNAS_16(Settings); + + Settings settings; + + virtual void DrawSettings() override; + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + virtual void SetupResources() override; + + ID3D11VertexShader* GetTerrainVertexShader(); + ID3D11VertexShader* GetTerrainOffsetVertexShader(); + + ID3D11VertexShader* terrainVertexShader = nullptr; + ID3D11VertexShader* terrainOffsetVertexShader = nullptr; + + ID3D11ComputeShader* GetDepthBlendShader(); + + virtual void PostPostLoad() override; + virtual void DataLoaded() override; + + bool renderDepth = false; + bool renderTerrainDepth = false; + bool renderAltTerrain = false; + + RE::NiPoint3 averageEyePosition; + + struct RenderPass + { + RE::BSRenderPass* a_pass; + uint32_t a_technique; + bool a_alphaTest; + uint32_t a_renderFlags; + }; + + std::vector renderPasses; + std::vector terrainRenderPasses; + + void TerrainShaderHacks(); + + void ResetDepth(); + void ResetTerrainDepth(); + void BlendPrepassDepths(); + + Texture2D* blendedDepthTexture = nullptr; + Texture2D* blendedDepthTexture16 = nullptr; + + ID3D11ShaderResourceView* GetBlendedDepthSRV() const + { + if (blendedDepthTexture && blendedDepthTexture->srv) + return blendedDepthTexture->srv.get(); + return nullptr; + } + + RE::BSGraphics::DepthStencilData terrainDepth; + + ID3D11DepthStencilState* terrainDepthStencilState = nullptr; + + ID3D11ShaderResourceView* depthSRVBackup = nullptr; + ID3D11ShaderResourceView* prepassSRVBackup = nullptr; + + ID3D11ComputeShader* depthBlendShader = nullptr; + + virtual void ClearShaderCache() override; + + void RenderTerrainBlendingPasses(); + void OnBeginTechnique(RE::BSShader* a_shader, uint32_t a_pixelDescriptor, uint32_t a_callerRva = 0); + void OnShadowmaskPhaseEnd(); + void OnUtilitySetupGeometry(RE::BSShader* a_shader, RE::BSRenderPass* a_pass, uint32_t a_renderFlags, uint32_t a_callerRva = 0); + void OnShaderPropertySetupGeometry(RE::BSShaderProperty* a_shaderProperty, RE::BSGeometry* a_geometry, bool a_result, uint32_t a_callerRva = 0); + void OnSetDirtyStates(bool a_isCompute, uint32_t a_callerRva = 0); + + struct Hooks + { + struct Main_RenderDepth + { + static void thunk(bool a1, bool a2); + static inline REL::Relocation func; + }; + + struct Main_RenderShadowmasks + { + static void thunk(bool a1); + static inline REL::Relocation func; + }; + + struct BSBatchRenderer__RenderPassImmediately + { + static void thunk(RE::BSRenderPass* a_pass, uint32_t a_technique, bool a_alphaTest, uint32_t a_renderFlags); + static inline REL::Relocation func; + }; + + struct BSUtilityShader_SetupGeometry + { + static void thunk(RE::BSShader* a_shader, RE::BSRenderPass* a_pass, uint32_t a_renderFlags); + static inline REL::Relocation func; + }; + + struct BSShaderProperty_SetupGeometry + { + static bool thunk(RE::BSShaderProperty* a_shaderProperty, RE::BSGeometry* a_geometry); + static inline REL::Relocation func; + }; + + struct BSShader_BeginTechnique + { + static bool thunk(RE::BSShader* shader, uint32_t vertexDescriptor, uint32_t pixelDescriptor, bool skipPixelShader); + static inline REL::Relocation func; + }; + + struct BSGraphics_SetDirtyStates + { + static void thunk(bool isCompute); + static inline REL::Relocation func; + }; + + static void Install() + { + // To know when we are rendering z-prepass depth vs shadows depth + stl::write_thunk_call(REL::RelocationID(35560, 36559).address() + REL::Relocate(0x395, 0x395, 0x2EE)); + + // To know when shadowmask phase ends (for releasing engine hook overrides) + stl::detour_thunk(REL::RelocationID(100422, 107140)); + + // To manipulate the depth buffer write, depth testing, alpha blending + stl::write_thunk_call(REL::RelocationID(100852, 107642).address() + REL::Relocate(0x29E, 0x28F)); + + // Engine path: late Utility setup hook so slot rebinding survives to draw. + stl::write_vfunc<0x6, BSUtilityShader_SetupGeometry>(RE::VTABLE_BSUtilityShader[0]); + + // Engine path: even later material/property setup hook for final slot correction. + stl::write_vfunc<0x27, BSShaderProperty_SetupGeometry>(RE::VTABLE_BSShaderProperty[0]); + + // Chained on top of Hooks.cpp's detours to intercept BeginTechnique/SetDirtyStates + // for engine SRV slot override during the shadowmask phase. + stl::detour_thunk(REL::RelocationID(101341, 108328)); + stl::detour_thunk(REL::RelocationID(75580, 77386)); + + logger::info("[Terrain Blending] Installed hooks"); + } + }; +}; diff --git a/src/Features/TerrainHelper.h b/src/Features/TerrainHelper.h index 1e3ca470cb..a54efd702c 100644 --- a/src/Features/TerrainHelper.h +++ b/src/Features/TerrainHelper.h @@ -1,47 +1,43 @@ -#pragma once - -struct TerrainHelper : Feature -{ -private: - static constexpr std::string_view MOD_ID = "143149"; - -public: - virtual inline std::string GetName() override { return "Terrain Helper"; } - virtual inline std::string GetShortName() override { return "TerrainHelper"; } - virtual inline std::string_view GetShaderDefineName() override { return "TERRAIN_HELPER"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kLandscapeAndTextures; } - - virtual std::pair> GetFeatureSummary() override - { - return { - "Provides enhanced terrain material support for terrain mods that require additional texture slots and parallax mapping capabilities.", - { "Extended texture slot support for terrain materials", - "Parallax mapping integration for terrain textures", - "Automatic terrain material detection and setup", - "Support for advanced terrain modifications", - "Compatibility layer for terrain enhancement mods" } - }; - } - - struct Settings - { - } settings; - - struct ExtendedSlots - { - std::array parallax; - }; - - std::shared_mutex extendedSlotsMutex; - std::unordered_map extendedSlots; - RE::BGSTextureSet* defaultLandTexture; - bool enabled = false; - - virtual void DataLoaded() override; - virtual bool SupportsVR() override { return true; }; - virtual std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - - void SetShaderResouces(ID3D11DeviceContext* a_context); - bool TESObjectLAND_SetupMaterial(RE::TESObjectLAND* land); - void BSLightingShader_SetupMaterial(RE::BSLightingShaderMaterialBase const* material); +#pragma once + +struct TerrainHelper : Feature +{ +private: +public: + virtual inline std::string GetName() override { return "Terrain Helper"; } + virtual inline std::string GetShortName() override { return "TerrainHelper"; } + virtual inline std::string_view GetShaderDefineName() override { return "TERRAIN_HELPER"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kLandscapeAndTextures; } + + virtual std::pair> GetFeatureSummary() override + { + return { + "Provides enhanced terrain material support for terrain mods that require additional texture slots and parallax mapping capabilities.", + { "Extended texture slot support for terrain materials", + "Parallax mapping integration for terrain textures", + "Automatic terrain material detection and setup", + "Support for advanced terrain modifications", + "Compatibility layer for terrain enhancement mods" } + }; + } + + struct Settings + { + } settings; + + struct ExtendedSlots + { + std::array parallax; + }; + + std::shared_mutex extendedSlotsMutex; + std::unordered_map extendedSlots; + RE::BGSTextureSet* defaultLandTexture; + bool enabled = false; + + virtual void DataLoaded() override; + virtual bool SupportsVR() override { return true; }; + void SetShaderResouces(ID3D11DeviceContext* a_context); + bool TESObjectLAND_SetupMaterial(RE::TESObjectLAND* land); + void BSLightingShader_SetupMaterial(RE::BSLightingShaderMaterialBase const* material); }; \ No newline at end of file diff --git a/src/Features/TerrainShadows.h b/src/Features/TerrainShadows.h index 0a636b7080..7b17c782c8 100644 --- a/src/Features/TerrainShadows.h +++ b/src/Features/TerrainShadows.h @@ -1,100 +1,97 @@ -#pragma once - -#include "Buffer.h" -#include - -struct TerrainShadows : public Feature -{ -private: - static constexpr std::string_view MOD_ID = "135817"; - -public: - virtual inline std::string GetName() override { return "Terrain Shadows"; } - virtual inline std::string GetShortName() override { return "TerrainShadows"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual inline std::string_view GetShaderDefineName() override { return "TERRAIN_SHADOWS"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kLandscapeAndTextures; } - virtual std::pair> GetFeatureSummary() override - { - return { - "Adds realistic shadow casting from terrain features using heightmap data to create accurate terrain shadows that enhance depth perception and visual realism.", - { "Heightmap-based terrain shadow calculation", - "Dynamic shadow updates based on sun position", - "Support for custom heightmap files", - "Real-time shadow preprocessing and computation", - "Integration with existing shadow systems" } - }; - } - virtual inline bool HasShaderDefine(RE::BSShader::Type) override { return true; } - - struct Settings - { - bool EnableTerrainShadow = true; - } settings; - - bool needPrecompute = false; - uint shadowUpdateIdx = 0; - - struct HeightMapMetadata - { - std::wstring dir; - std::string filename; - std::string worldspace; - float3 pos0, pos1; // left-top-z=0 vs right-bottom-z=1 - float2 zRange; - }; - std::unordered_map heightmaps; - HeightMapMetadata* cachedHeightmap; - - struct ShadowUpdateCB - { - float2 LightPxDir; // direction on which light descends, from one pixel to next via dda - float2 LightDeltaZ; // per LightUVDir, upper penumbra and lower, should be negative - uint StartPxCoord; - float2 PxSize; - uint pad0[1]; - float2 PosRange; - float2 ZRange; - } shadowUpdateCBData; - static_assert(sizeof(ShadowUpdateCB) % 16 == 0); - std::unique_ptr shadowUpdateCB = nullptr; - - struct alignas(16) PerFrame - { - uint EnableTerrainShadow; - float3 Scale; - float2 ZRange; - float2 Offset; - }; - STATIC_ASSERT_ALIGNAS_16(PerFrame); - - PerFrame GetCommonBufferData(); - - winrt::com_ptr shadowUpdateProgram = nullptr; - - std::unique_ptr texHeightMap = nullptr; - std::unique_ptr texShadowHeight = nullptr; - - bool IsHeightMapReady(); - - virtual void SetupResources() override; - void ParseHeightmapPath(std::filesystem::path p, bool xlodgen_style); - void CompileComputeShaders(); - - virtual void DrawSettings() override; - - virtual void EarlyPrepass() override; - void LoadHeightmap(); - void Precompute(); - void UpdateShadow(); - - virtual void ReflectionsPrepass() override; - - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - - virtual inline void RestoreDefaultSettings() override { settings = {}; } - virtual void ClearShaderCache() override; - virtual bool SupportsVR() override { return true; }; - virtual bool IsCore() const override { return true; }; +#pragma once + +#include "Buffer.h" +#include + +struct TerrainShadows : public Feature +{ +private: +public: + virtual inline std::string GetName() override { return "Terrain Shadows"; } + virtual inline std::string GetShortName() override { return "TerrainShadows"; } + virtual inline std::string_view GetShaderDefineName() override { return "TERRAIN_SHADOWS"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kLandscapeAndTextures; } + virtual std::pair> GetFeatureSummary() override + { + return { + "Adds realistic shadow casting from terrain features using heightmap data to create accurate terrain shadows that enhance depth perception and visual realism.", + { "Heightmap-based terrain shadow calculation", + "Dynamic shadow updates based on sun position", + "Support for custom heightmap files", + "Real-time shadow preprocessing and computation", + "Integration with existing shadow systems" } + }; + } + virtual inline bool HasShaderDefine(RE::BSShader::Type) override { return true; } + + struct Settings + { + bool EnableTerrainShadow = true; + } settings; + + bool needPrecompute = false; + uint shadowUpdateIdx = 0; + + struct HeightMapMetadata + { + std::wstring dir; + std::string filename; + std::string worldspace; + float3 pos0, pos1; // left-top-z=0 vs right-bottom-z=1 + float2 zRange; + }; + std::unordered_map heightmaps; + HeightMapMetadata* cachedHeightmap; + + struct ShadowUpdateCB + { + float2 LightPxDir; // direction on which light descends, from one pixel to next via dda + float2 LightDeltaZ; // per LightUVDir, upper penumbra and lower, should be negative + uint StartPxCoord; + float2 PxSize; + uint pad0[1]; + float2 PosRange; + float2 ZRange; + } shadowUpdateCBData; + static_assert(sizeof(ShadowUpdateCB) % 16 == 0); + std::unique_ptr shadowUpdateCB = nullptr; + + struct alignas(16) PerFrame + { + uint EnableTerrainShadow; + float3 Scale; + float2 ZRange; + float2 Offset; + }; + STATIC_ASSERT_ALIGNAS_16(PerFrame); + + PerFrame GetCommonBufferData(); + + winrt::com_ptr shadowUpdateProgram = nullptr; + + std::unique_ptr texHeightMap = nullptr; + std::unique_ptr texShadowHeight = nullptr; + + bool IsHeightMapReady(); + + virtual void SetupResources() override; + void ParseHeightmapPath(std::filesystem::path p, bool xlodgen_style); + void CompileComputeShaders(); + + virtual void DrawSettings() override; + + virtual void EarlyPrepass() override; + void LoadHeightmap(); + void Precompute(); + void UpdateShadow(); + + virtual void ReflectionsPrepass() override; + + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + virtual inline void RestoreDefaultSettings() override { settings = {}; } + virtual void ClearShaderCache() override; + virtual bool SupportsVR() override { return true; }; + virtual bool IsCore() const override { return true; }; }; \ No newline at end of file diff --git a/src/Features/TerrainVariation.h b/src/Features/TerrainVariation.h index 1434b8d4de..c6bccb784e 100644 --- a/src/Features/TerrainVariation.h +++ b/src/Features/TerrainVariation.h @@ -1,48 +1,45 @@ -#pragma once - -struct TerrainVariation : Feature -{ -private: - static constexpr std::string_view MOD_ID = "148123"; - -public: - virtual inline std::string GetName() override { return "Terrain Variation"; } - virtual inline std::string GetShortName() override { return "TerrainVariation"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual inline std::string_view GetShaderDefineName() override { return "TERRAIN_VARIATION"; } - virtual inline bool HasShaderDefine(RE::BSShader::Type shaderType) override - { - return (shaderType == RE::BSShader::Type::Lighting); - } - virtual bool IsCore() const override { return false; }; - virtual bool SupportsVR() override { return true; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kLandscapeAndTextures; } - - virtual std::pair> GetFeatureSummary() override - { - return { - "Terrain Variation reduces the repeating pattern effect on terrain textures.\n" - "This technique creates more natural-looking terrain by adding variation to texture sampling.", - { "Reduces terrain texture tiling", - "Adjustable distance-based blending", - "Improved terrain visual quality", - "Compatible with Extended Materials parallax" } - }; - } - - struct Settings - { - uint enableTilingFix = true; - uint enableLODTerrainTilingFix = true; - float pad0[2]; - } settings; - - virtual void DrawSettings() override; - virtual bool DrawFailLoadMessage() const override; - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - virtual void RestoreDefaultSettings() override; - - virtual void PostPostLoad() override; - void UpdateShaderSettings(); +#pragma once + +struct TerrainVariation : Feature +{ +private: +public: + virtual inline std::string GetName() override { return "Terrain Variation"; } + virtual inline std::string GetShortName() override { return "TerrainVariation"; } + virtual inline std::string_view GetShaderDefineName() override { return "TERRAIN_VARIATION"; } + virtual inline bool HasShaderDefine(RE::BSShader::Type shaderType) override + { + return (shaderType == RE::BSShader::Type::Lighting); + } + virtual bool IsCore() const override { return false; }; + virtual bool SupportsVR() override { return true; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kLandscapeAndTextures; } + + virtual std::pair> GetFeatureSummary() override + { + return { + "Terrain Variation reduces the repeating pattern effect on terrain textures.\n" + "This technique creates more natural-looking terrain by adding variation to texture sampling.", + { "Reduces terrain texture tiling", + "Adjustable distance-based blending", + "Improved terrain visual quality", + "Compatible with Extended Materials parallax" } + }; + } + + struct Settings + { + uint enableTilingFix = true; + uint enableLODTerrainTilingFix = true; + float pad0[2]; + } settings; + + virtual void DrawSettings() override; + virtual bool DrawFailLoadMessage() const override; + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + virtual void RestoreDefaultSettings() override; + + virtual void PostPostLoad() override; + void UpdateShaderSettings(); }; \ No newline at end of file diff --git a/src/Features/Upscaling.h b/src/Features/Upscaling.h index 1e88e99937..4103bcdcc7 100644 --- a/src/Features/Upscaling.h +++ b/src/Features/Upscaling.h @@ -1,276 +1,276 @@ -#pragma once - -#include "Feature.h" -#include "Upscaling/DX12SwapChain.h" -#include "Upscaling/FidelityFX.h" -#include "Upscaling/RCAS/RCAS.h" -#include "Upscaling/Streamline.h" -#include -#include -#include - -/** - * @brief Provides upscaling functionality including DLSS, FSR and TAA. - * - * This feature handles various upscaling methods and frame generation technologies - * to improve performance while maintaining visual quality. - */ -struct Upscaling : Feature -{ -public: - // Feature interface - virtual inline std::string GetName() override { return "Upscaling"; } - virtual inline std::string GetShortName() override { return "Upscaling"; } - virtual inline bool SupportsVR() override { return true; } - virtual inline bool IsCore() const override { return false; } - virtual inline std::string_view GetCategory() const override { return FeatureCategories::kDisplay; } - - virtual std::pair> GetFeatureSummary() override - { - return { - "Advanced upscaling and frame generation technologies for improved performance", - { "DLSS (Deep Learning Super Sampling) support", - "FSR (FidelityFX Super Resolution) support", - "TAA (Temporal Anti-Aliasing) support", - "Frame generation for supported systems" } - }; - } - - float2 jitter = { 0, 0 }; - - enum class UpscaleMethod - { - kNONE, - kTAA, - kFSR, - kDLSS - }; - - struct Settings - { - uint upscaleMethod = (uint)UpscaleMethod::kDLSS; - uint upscaleMethodNoDLSS = (uint)UpscaleMethod::kFSR; - uint qualityMode = 1; // Default to Quality (1=Quality, 2=Balanced, 3=Performance, 4=Ultra Performance, 0=Native AA) - uint frameLimitMode = 1; - uint frameGenerationMode = 1; - uint frameGenerationForceEnable = 0; - uint streamlineLogLevel = 0; // 0=Off, 1=Default, 2=Verbose - float sharpnessFSR = 0.0f; - float sharpnessDLSS = 0.0f; - uint presetDLSS = 0; // 0=Default, 1=J, 2=K, 3=L, 4=M - uint useGatherWideKernel = 1; // 0=Legacy 3x3, 1=Gather wide-kernel - }; - - Settings settings; - - struct JitterCB - { - float2 jitter; - float useWideKernel; - float useGatherWideKernel; - }; - - struct UpscalingDataCB - { - float2 trueSamplingDim; // BufferDim.xy * ResolutionScale - float2 pad0; - }; - - ConstantBuffer* jitterCB = nullptr; - ConstantBuffer* upscalingDataCB = nullptr; - - // Runtime state - bool isWindowed = false; - bool lowRefreshRate = false; - bool fidelityFXMissing = false; - bool d3d12SwapChainActive = false; - - // Timing and scaling - double refreshRate = 0.0f; - float2 resolutionScale = { 1.0f, 1.0f }; - LARGE_INTEGER qpf; - - // FG FPS Measurement for Overlay - bool IsFrameGenerationActive() const; - float GetFrameGenerationFrameTime() const; - bool IsUpscalingActive() const; - - // Feature interface overrides - virtual void DrawSettings() override; - virtual void SaveSettings(json& o_json) override; - virtual void LoadSettings(json& o_json) override; - virtual void RestoreDefaultSettings() override; - virtual void DataLoaded() override; - - /** - * @brief Installs Direct3D-related hooks for device and factory creation. - * - * Loads FidelityFX support and patches the import address table (IAT) to redirect D3D11 device and DXGI factory creation functions to custom hook implementations. - **/ - virtual void Load() override; - virtual void PostPostLoad() override; - virtual void SetupResources() override; - - UpscaleMethod GetUpscaleMethod() const; - - void CheckResources(UpscaleMethod a_upscalemethod); - void CreateUpscalingTextureResources(UpscaleMethod a_upscalemethod); - void DestroyUpscalingTextureResources(UpscaleMethod a_upscalemethod); - - winrt::com_ptr encodeTexturesCS[5]; // One for each UpscaleMethod - ID3D11ComputeShader* GetEncodeTexturesCS(); - - winrt::com_ptr depthRefractionUpscalePS; - ID3D11PixelShader* GetDepthRefractionUpscalePS(); - - winrt::com_ptr underwaterMaskUpscalePS; - ID3D11PixelShader* GetUnderwaterMaskUpscalePS(); - - winrt::com_ptr upscaleVS; - ID3D11VertexShader* GetUpscaleVS(); - - winrt::com_ptr upscaleDepthStencilState; - winrt::com_ptr upscaleBlendState; - winrt::com_ptr upscaleRasterizerState; - - // Shared VR HMD Mask Clearing - winrt::com_ptr vrClearHMDMaskCS; - winrt::com_ptr vrClearHMDMaskCB; - // Helper to dispatch mask clearing for a single eye region - void ClearHMDMask(ID3D11UnorderedAccessView* colorUAV, ID3D11ShaderResourceView* depthSRV, - uint32_t eyeWidth, uint32_t eyeHeight, uint32_t depthOffsetX, uint32_t colorOffsetX); - - // Shared VR Per-Eye Intermediate Buffers - // Owned here so both Streamline (DLSS) and FidelityFX (FSR) can use them. - eastl::unique_ptr vrIntermediateColorIn[2]; // per-eye render resolution - eastl::unique_ptr vrIntermediateColorOut[2]; // per-eye output resolution - eastl::unique_ptr vrIntermediateDepth[2]; // per-eye render resolution - eastl::unique_ptr vrIntermediateMotionVectors[2]; // per-eye render resolution - eastl::unique_ptr vrIntermediateReactiveMask[2]; // per-eye render resolution - eastl::unique_ptr vrIntermediateTransparencyMask[2]; // per-eye render resolution - - // Helper to create/resize per-eye buffers matching source formats - void CreateVRIntermediateTextures(uint32_t inWidth, uint32_t inHeight, uint32_t outWidth, uint32_t outHeight, - ID3D11Resource* colorSrc, ID3D11Resource* mvecSrc, ID3D11Resource* reactiveSrc, ID3D11Resource* transparencySrc); - - // Helper: Create a Texture2D matching source format at a given size - static eastl::unique_ptr CreateTextureFromSource(ID3D11Resource* src, uint32_t width, uint32_t height, - bool copyBindFlags = false, bool createSRV = false, bool createUAV = false, const char* name = nullptr); - - // Shared Pipeline Steps - void PreparePerEyeInputs(ID3D11Resource* colorSrc, ID3D11Resource* depthSrc, ID3D11Resource* mvecSrc, - ID3D11Resource* reactiveSrc, ID3D11Resource* transparencySrc); - void FinalizePerEyeOutputs(ID3D11Resource* colorDst); - - void ConfigureTAA(); - void ConfigureUpscaling(RE::BSGraphics::State* a_state); - void Upscale(); - - // D3D11 textures - Texture2D* reactiveMaskTexture = nullptr; - Texture2D* transparencyCompositionMaskTexture = nullptr; - Texture2D* motionVectorCopyTexture = nullptr; - Texture2D* sharpenerTexture = nullptr; - - virtual void ClearShaderCache() override; - - // Static instances instead of singletons - static inline Streamline streamline; - static inline FidelityFX fidelityFX; ///< Only for frame generation - static inline DX12SwapChain dx12SwapChain; - static inline RCAS rcas; ///< Standalone RCAS sharpening for DLSS - - winrt::com_ptr copyDepthToSharedBufferPS; - - float projectionPosScaleX = 0.0f; - float projectionPosScaleY = 0.0f; - - float dynamicResolutionWidthRatio = 1.0f; - float dynamicResolutionHeightRatio = 1.0f; - - bool previousUpscalingWasActive = false; - bool depthUpscaleUseWideKernel = false; - - void CopySharedD3D12Resources(); - void PostDisplay(); - void PerformUpscaling(); - void UpscaleDepth(); - - /** - * @brief Applies RCAS sharpening to the main render target after DLSS upscaling. - * - * Runs in HDR space before tonemapping. Only called when DLSS is active and sharpness > 0. - */ - void ApplySharpening(); - - static void TimerSleepQPC(int64_t targetQPC); - - void FrameLimiter(); - - static double GetRefreshRate(HWND a_window); - - // Unified interface methods - external code should use these instead of direct access - void LoadUpscalingSDKs(); // Loads all SDKs at once - void SetUIBuffer(); - HANDLE GetFrameLatencyWaitableObject() const; - float GetFrameTime() const; - - // Backend interface methods - bool IsBackendInitialized() const; - void CheckBackendFeatures(IDXGIAdapter* adapter); - void UpgradeBackendInterface(void** ppInterface); - void SetBackendD3DDevice(ID3D11Device* device); - void PostBackendDevice(); - - // Module availability methods - bool HasFrameGenModule() const; - - // Proxy interface methods - void SetProxyD3D11Device(ID3D11Device* device); - void SetProxyD3D11DeviceContext(ID3D11DeviceContext* context); - void CreateProxySwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC swapChainDesc); - void CreateProxyInterop(); - IDXGISwapChain* GetProxySwapChain(); - - using BlurResources = DX12SwapChain::BlurResources; - - // Get all D3D11 resources needed for background blur when D3D12 swap chain is active - BlurResources GetBlurResources() const; - -private: - struct Main_UpdateJitter - { - static void thunk(RE::BSGraphics::State* a_state); - static inline REL::Relocation func; - }; - - struct MenuManagerDrawInterfaceStartHook - { - static void thunk(int64_t a1); - static inline REL::Relocation func; - }; - - struct Main_PostProcessing - { - static void thunk(RE::ImageSpaceManager* a_this, uint32_t a3, RE::RENDER_TARGET a_target, void* a_4, bool a_5); - static inline REL::Relocation func; - }; - - struct SetScissorRect - { - static void thunk(RE::BSGraphics::Renderer* This, int a_left, int a_top, int a_right, int a_bottom); - static inline REL::Relocation func; - }; - - struct Main_RenderPrecipitation - { - static void thunk(); - static inline REL::Relocation func; - }; - - struct BSFaceGenManager_UpdatePendingCustomizationTextures - { - static void thunk(); - static inline REL::Relocation func; - }; -}; +#pragma once + +#include "Feature.h" +#include "Upscaling/DX12SwapChain.h" +#include "Upscaling/FidelityFX.h" +#include "Upscaling/RCAS/RCAS.h" +#include "Upscaling/Streamline.h" +#include +#include +#include + +/** + * @brief Provides upscaling functionality including DLSS, FSR and TAA. + * + * This feature handles various upscaling methods and frame generation technologies + * to improve performance while maintaining visual quality. + */ +struct Upscaling : Feature +{ +public: + // Feature interface + virtual inline std::string GetName() override { return "Upscaling"; } + virtual inline std::string GetShortName() override { return "Upscaling"; } + virtual inline bool SupportsVR() override { return true; } + virtual inline bool IsCore() const override { return false; } + virtual inline std::string_view GetCategory() const override { return FeatureCategories::kDisplay; } + + virtual std::pair> GetFeatureSummary() override + { + return { + "Advanced upscaling and frame generation technologies for improved performance", + { "DLSS (Deep Learning Super Sampling) support", + "FSR (FidelityFX Super Resolution) support", + "TAA (Temporal Anti-Aliasing) support", + "Frame generation for supported systems" } + }; + } + + float2 jitter = { 0, 0 }; + + enum class UpscaleMethod + { + kNONE, + kTAA, + kFSR, + kDLSS + }; + + struct Settings + { + uint upscaleMethod = (uint)UpscaleMethod::kDLSS; + uint upscaleMethodNoDLSS = (uint)UpscaleMethod::kFSR; + uint qualityMode = 1; // Default to Quality (1=Quality, 2=Balanced, 3=Performance, 4=Ultra Performance, 0=Native AA) + uint frameLimitMode = 1; + uint frameGenerationMode = 1; + uint frameGenerationForceEnable = 0; + uint streamlineLogLevel = 0; // 0=Off, 1=Default, 2=Verbose + float sharpnessFSR = 0.0f; + float sharpnessDLSS = 0.0f; + uint presetDLSS = 0; // 0=Default, 1=J, 2=K, 3=L, 4=M + uint useGatherWideKernel = 1; // 0=Legacy 3x3, 1=Gather wide-kernel + }; + + Settings settings; + + struct JitterCB + { + float2 jitter; + float useWideKernel; + float useGatherWideKernel; + }; + + struct UpscalingDataCB + { + float2 trueSamplingDim; // BufferDim.xy * ResolutionScale + float2 pad0; + }; + + ConstantBuffer* jitterCB = nullptr; + ConstantBuffer* upscalingDataCB = nullptr; + + // Runtime state + bool isWindowed = false; + bool lowRefreshRate = false; + bool fidelityFXMissing = false; + bool d3d12SwapChainActive = false; + + // Timing and scaling + double refreshRate = 0.0f; + float2 resolutionScale = { 1.0f, 1.0f }; + LARGE_INTEGER qpf; + + // FG FPS Measurement for Overlay + bool IsFrameGenerationActive() const; + float GetFrameGenerationFrameTime() const; + bool IsUpscalingActive() const; + + // Feature interface overrides + virtual void DrawSettings() override; + virtual void SaveSettings(json& o_json) override; + virtual void LoadSettings(json& o_json) override; + virtual void RestoreDefaultSettings() override; + virtual void DataLoaded() override; + + /** + * @brief Installs Direct3D-related hooks for device and factory creation. + * + * Loads FidelityFX support and patches the import address table (IAT) to redirect D3D11 device and DXGI factory creation functions to custom hook implementations. + **/ + virtual void Load() override; + virtual void PostPostLoad() override; + virtual void SetupResources() override; + + UpscaleMethod GetUpscaleMethod() const; + + void CheckResources(UpscaleMethod a_upscalemethod); + void CreateUpscalingTextureResources(UpscaleMethod a_upscalemethod); + void DestroyUpscalingTextureResources(UpscaleMethod a_upscalemethod); + + winrt::com_ptr encodeTexturesCS[5]; // One for each UpscaleMethod + ID3D11ComputeShader* GetEncodeTexturesCS(); + + winrt::com_ptr depthRefractionUpscalePS; + ID3D11PixelShader* GetDepthRefractionUpscalePS(); + + winrt::com_ptr underwaterMaskUpscalePS; + ID3D11PixelShader* GetUnderwaterMaskUpscalePS(); + + winrt::com_ptr upscaleVS; + ID3D11VertexShader* GetUpscaleVS(); + + winrt::com_ptr upscaleDepthStencilState; + winrt::com_ptr upscaleBlendState; + winrt::com_ptr upscaleRasterizerState; + + // Shared VR HMD Mask Clearing + winrt::com_ptr vrClearHMDMaskCS; + winrt::com_ptr vrClearHMDMaskCB; + // Helper to dispatch mask clearing for a single eye region + void ClearHMDMask(ID3D11UnorderedAccessView* colorUAV, ID3D11ShaderResourceView* depthSRV, + uint32_t eyeWidth, uint32_t eyeHeight, uint32_t depthOffsetX, uint32_t colorOffsetX); + + // Shared VR Per-Eye Intermediate Buffers + // Owned here so both Streamline (DLSS) and FidelityFX (FSR) can use them. + eastl::unique_ptr vrIntermediateColorIn[2]; // per-eye render resolution + eastl::unique_ptr vrIntermediateColorOut[2]; // per-eye output resolution + eastl::unique_ptr vrIntermediateDepth[2]; // per-eye render resolution + eastl::unique_ptr vrIntermediateMotionVectors[2]; // per-eye render resolution + eastl::unique_ptr vrIntermediateReactiveMask[2]; // per-eye render resolution + eastl::unique_ptr vrIntermediateTransparencyMask[2]; // per-eye render resolution + + // Helper to create/resize per-eye buffers matching source formats + void CreateVRIntermediateTextures(uint32_t inWidth, uint32_t inHeight, uint32_t outWidth, uint32_t outHeight, + ID3D11Resource* colorSrc, ID3D11Resource* mvecSrc, ID3D11Resource* reactiveSrc, ID3D11Resource* transparencySrc); + + // Helper: Create a Texture2D matching source format at a given size + static eastl::unique_ptr CreateTextureFromSource(ID3D11Resource* src, uint32_t width, uint32_t height, + bool copyBindFlags = false, bool createSRV = false, bool createUAV = false, const char* name = nullptr); + + // Shared Pipeline Steps + void PreparePerEyeInputs(ID3D11Resource* colorSrc, ID3D11Resource* depthSrc, ID3D11Resource* mvecSrc, + ID3D11Resource* reactiveSrc, ID3D11Resource* transparencySrc); + void FinalizePerEyeOutputs(ID3D11Resource* colorDst); + + void ConfigureTAA(); + void ConfigureUpscaling(RE::BSGraphics::State* a_state); + void Upscale(); + + // D3D11 textures + Texture2D* reactiveMaskTexture = nullptr; + Texture2D* transparencyCompositionMaskTexture = nullptr; + Texture2D* motionVectorCopyTexture = nullptr; + Texture2D* sharpenerTexture = nullptr; + + virtual void ClearShaderCache() override; + + // Static instances instead of singletons + static inline Streamline streamline; + static inline FidelityFX fidelityFX; ///< Only for frame generation + static inline DX12SwapChain dx12SwapChain; + static inline RCAS rcas; ///< Standalone RCAS sharpening for DLSS + + winrt::com_ptr copyDepthToSharedBufferPS; + + float projectionPosScaleX = 0.0f; + float projectionPosScaleY = 0.0f; + + float dynamicResolutionWidthRatio = 1.0f; + float dynamicResolutionHeightRatio = 1.0f; + + bool previousUpscalingWasActive = false; + bool depthUpscaleUseWideKernel = false; + + void CopySharedD3D12Resources(); + void PostDisplay(); + void PerformUpscaling(); + void UpscaleDepth(); + + /** + * @brief Applies RCAS sharpening to the main render target after DLSS upscaling. + * + * Runs in HDR space before tonemapping. Only called when DLSS is active and sharpness > 0. + */ + void ApplySharpening(); + + static void TimerSleepQPC(int64_t targetQPC); + + void FrameLimiter(); + + static double GetRefreshRate(HWND a_window); + + // Unified interface methods - external code should use these instead of direct access + void LoadUpscalingSDKs(); // Loads all SDKs at once + void SetUIBuffer(); + HANDLE GetFrameLatencyWaitableObject() const; + float GetFrameTime() const; + + // Backend interface methods + bool IsBackendInitialized() const; + void CheckBackendFeatures(IDXGIAdapter* adapter); + void UpgradeBackendInterface(void** ppInterface); + void SetBackendD3DDevice(ID3D11Device* device); + void PostBackendDevice(); + + // Module availability methods + bool HasFrameGenModule() const; + + // Proxy interface methods + void SetProxyD3D11Device(ID3D11Device* device); + void SetProxyD3D11DeviceContext(ID3D11DeviceContext* context); + void CreateProxySwapChain(IDXGIAdapter* adapter, DXGI_SWAP_CHAIN_DESC swapChainDesc); + void CreateProxyInterop(); + IDXGISwapChain* GetProxySwapChain(); + + using BlurResources = DX12SwapChain::BlurResources; + + // Get all D3D11 resources needed for background blur when D3D12 swap chain is active + BlurResources GetBlurResources() const; + +private: + struct Main_UpdateJitter + { + static void thunk(RE::BSGraphics::State* a_state); + static inline REL::Relocation func; + }; + + struct MenuManagerDrawInterfaceStartHook + { + static void thunk(int64_t a1); + static inline REL::Relocation func; + }; + + struct Main_PostProcessing + { + static void thunk(RE::ImageSpaceManager* a_this, uint32_t a3, RE::RENDER_TARGET a_target, void* a_4, bool a_5); + static inline REL::Relocation func; + }; + + struct SetScissorRect + { + static void thunk(RE::BSGraphics::Renderer* This, int a_left, int a_top, int a_right, int a_bottom); + static inline REL::Relocation func; + }; + + struct Main_RenderPrecipitation + { + static void thunk(); + static inline REL::Relocation func; + }; + + struct BSFaceGenManager_UpdatePendingCustomizationTextures + { + static void thunk(); + static inline REL::Relocation func; + }; +}; diff --git a/src/Features/WaterEffects.h b/src/Features/WaterEffects.h index 2036418be7..b3a608f50a 100644 --- a/src/Features/WaterEffects.h +++ b/src/Features/WaterEffects.h @@ -1,39 +1,36 @@ -#pragma once - -#include - -struct WaterEffects : Feature -{ -private: - static constexpr std::string_view MOD_ID = "112762"; - -public: - winrt::com_ptr causticsView; - virtual inline std::string GetName() override { return "Water Effects"; } - virtual inline std::string GetShortName() override { return "WaterEffects"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual inline std::string_view GetShaderDefineName() override { return "WATER_EFFECTS"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kWater; } - - virtual std::pair> GetFeatureSummary() override - { - return { - "Water Effects enhances water rendering with realistic caustics and underwater lighting effects.\n" - "This feature adds dynamic light patterns and improved water visual quality.", - { "Realistic water caustics", - "Enhanced underwater lighting", - "Dynamic light patterns on water surfaces", - "Improved water visual fidelity", - "Atmospheric underwater effects" } - }; - } - - bool HasShaderDefine(RE::BSShader::Type shaderType) override; - - virtual void SetupResources() override; - - virtual void Prepass() override; - - virtual bool SupportsVR() override { return true; }; - virtual bool IsCore() const override { return true; }; -}; +#pragma once + +#include + +struct WaterEffects : Feature +{ +private: +public: + winrt::com_ptr causticsView; + virtual inline std::string GetName() override { return "Water Effects"; } + virtual inline std::string GetShortName() override { return "WaterEffects"; } + virtual inline std::string_view GetShaderDefineName() override { return "WATER_EFFECTS"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kWater; } + + virtual std::pair> GetFeatureSummary() override + { + return { + "Water Effects enhances water rendering with realistic caustics and underwater lighting effects.\n" + "This feature adds dynamic light patterns and improved water visual quality.", + { "Realistic water caustics", + "Enhanced underwater lighting", + "Dynamic light patterns on water surfaces", + "Improved water visual fidelity", + "Atmospheric underwater effects" } + }; + } + + bool HasShaderDefine(RE::BSShader::Type shaderType) override; + + virtual void SetupResources() override; + + virtual void Prepass() override; + + virtual bool SupportsVR() override { return true; }; + virtual bool IsCore() const override { return true; }; +}; diff --git a/src/Features/WetnessEffects.h b/src/Features/WetnessEffects.h index e01e0bcc31..e65c1a7d1f 100644 --- a/src/Features/WetnessEffects.h +++ b/src/Features/WetnessEffects.h @@ -1,156 +1,153 @@ -#pragma once - -#include "Buffer.h" - -struct WetnessEffects : Feature -{ -private: - static constexpr std::string_view MOD_ID = "112739"; - -public: - virtual inline std::string GetName() override { return "Wetness Effects"; } - virtual inline std::string GetShortName() override { return "WetnessEffects"; } - virtual inline std::string GetFeatureModLink() override { return MakeNexusModURL(MOD_ID); } - virtual inline std::string_view GetShaderDefineName() override { return "WETNESS_EFFECTS"; } - virtual std::string_view GetCategory() const override { return FeatureCategories::kWater; } - - virtual std::pair> GetFeatureSummary() override - { - return { - "Adds realistic wetness effects including rain-based surface wetness, puddle formation, shore wetness, and dynamic raindrop effects for enhanced weather immersion.", - { "Dynamic surface wetness based on weather conditions", - "Realistic puddle formation and shore wetness effects", - "Animated raindrop effects with splashes and ripples", - "Configurable wetness intensity and weather transitions", - "Support for skin wetness and material-specific responses" } - }; - } - - bool HasShaderDefine(RE::BSShader::Type) override { return true; }; - - struct Settings - { - uint EnableWetnessEffects = true; - float MaxRainWetness = 1.0f; - float MaxPuddleWetness = 1.5f; - float MaxShoreWetness = 1.0f; - uint ShoreRange = 32; - float PuddleRadius = 1.0f; - float PuddleMaxAngle = 0.95f; - float PuddleMinWetness = 0.85f; - float MinRainWetness = 0.65f; - float SkinWetness = 0.95f; - float WeatherTransitionSpeed = 3.0f; - - // Raindrop fx settings - uint EnableRaindropFx = true; - uint EnableSplashes = true; - uint EnableRipples = true; - uint EnableVanillaRipples = false; - float RaindropFxRange = 1000.f; - float RaindropGridSize = 4.f; - float RaindropInterval = 1.0f; - float RaindropChance = 1.0f; - float SplashesLifetime = 10.0f; - float SplashesStrength = 1.05f; - float SplashesMinRadius = .3f; - float SplashesMaxRadius = .5f; - float RippleStrength = 1.f; - float RippleRadius = 1.f; - float RippleBreadth = .5f; - float RippleLifetime = .5f; - }; - - struct alignas(16) PerFrame - { - REX::W32::XMFLOAT4X4 OcclusionViewProj; - float Time; - float Raining; - float Wetness; - float PuddleWetness; - Settings settings; - uint pad0; - }; - STATIC_ASSERT_ALIGNAS_16(PerFrame); - - struct DebugSettings - { - bool EnableWetnessOverride = false; - bool EnablePuddleOverride = false; - bool EnableRainOverride = false; - bool EnableIntExOverride = false; - float2 WetnessOverride = float2(0.0f, 0.0f); - float2 PuddleWetnessOverride = float2(0.0f, 0.0f); - float2 RainOverride = float2(0.0f, 0.0f); - } debugSettings; - - Settings settings; - // Climate preset system - enum class ClimatePreset : uint32_t - { - Custom = 0, - Legacy = 1, - NordicStandard = 2, - ArcticTundra = 3, - TemperateCoastal = 4, - MonsoonExtreme = 5 - }; - struct ClimateSettings - { - float wetnessMultiplier; - float puddleMultiplier; - float transitionSpeed; - float raindropChance; - float raindropGridSize; - float raindropInterval; - }; - static constexpr ClimatePreset defaultPreset = ClimatePreset::NordicStandard; - ClimatePreset climatePreset = defaultPreset; - - PerFrame GetCommonBufferData() const; - - virtual void Prepass() override; - virtual void PostPostLoad() override; - - virtual void DrawSettings() override; - - virtual void LoadSettings(json& o_json) override; - virtual void SaveSettings(json& o_json) override; - - virtual void RestoreDefaultSettings() override; - - virtual bool SupportsVR() override { return true; }; - - // Override to provide weather analysis configuration - virtual WeatherAnalysisConfig GetWeatherAnalysisConfig() const override - { - return WeatherAnalysisConfig("Rain & Wetness Analysis", [this]() { - this->DrawWeatherAnalysis(); - }); - } - - // Constants and utilities for rain intensity calculations - static constexpr float MAX_RAIN_PARTICLE_DENSITY = 3.0f; - - // Helper function to extract rain intensity from precipitation object and weather - static float GetRainIntensity(RE::NiPointer precipObject, RE::TESWeather* weather); // Helper function to calculate precipitation rate from shader data and settings - float CalculatePrecipitationRate(float raindropChance, float raindropGridSizeGameUnits, float raindropIntervalSeconds, float mlPerDrop = 0.01f) const; - static const ClimateSettings& GetClimateSettings(ClimatePreset preset); - void ApplyClimatePreset(ClimatePreset preset); - bool DoesCurrentSettingsMatchPreset(ClimatePreset preset) const; - void DetectCurrentPreset(); - -private: - void DrawWeatherAnalysis() const; - - bool splashesOfStormsLoaded = false; - - // Weather wetness calculation result for debug display - struct WeatherWetnessResult - { - float wetness = 0.0f; - float puddleWetness = 0.0f; - }; - - WeatherWetnessResult CalculateWeatherWetness(RE::TESWeather* weather, float weatherPct, bool isCurrentWeather) const; -}; +#pragma once + +#include "Buffer.h" + +struct WetnessEffects : Feature +{ +private: +public: + virtual inline std::string GetName() override { return "Wetness Effects"; } + virtual inline std::string GetShortName() override { return "WetnessEffects"; } + virtual inline std::string_view GetShaderDefineName() override { return "WETNESS_EFFECTS"; } + virtual std::string_view GetCategory() const override { return FeatureCategories::kWater; } + + virtual std::pair> GetFeatureSummary() override + { + return { + "Adds realistic wetness effects including rain-based surface wetness, puddle formation, shore wetness, and dynamic raindrop effects for enhanced weather immersion.", + { "Dynamic surface wetness based on weather conditions", + "Realistic puddle formation and shore wetness effects", + "Animated raindrop effects with splashes and ripples", + "Configurable wetness intensity and weather transitions", + "Support for skin wetness and material-specific responses" } + }; + } + + bool HasShaderDefine(RE::BSShader::Type) override { return true; }; + + struct Settings + { + uint EnableWetnessEffects = true; + float MaxRainWetness = 1.0f; + float MaxPuddleWetness = 1.5f; + float MaxShoreWetness = 1.0f; + uint ShoreRange = 32; + float PuddleRadius = 1.0f; + float PuddleMaxAngle = 0.95f; + float PuddleMinWetness = 0.85f; + float MinRainWetness = 0.65f; + float SkinWetness = 0.95f; + float WeatherTransitionSpeed = 3.0f; + + // Raindrop fx settings + uint EnableRaindropFx = true; + uint EnableSplashes = true; + uint EnableRipples = true; + uint EnableVanillaRipples = false; + float RaindropFxRange = 1000.f; + float RaindropGridSize = 4.f; + float RaindropInterval = 1.0f; + float RaindropChance = 1.0f; + float SplashesLifetime = 10.0f; + float SplashesStrength = 1.05f; + float SplashesMinRadius = .3f; + float SplashesMaxRadius = .5f; + float RippleStrength = 1.f; + float RippleRadius = 1.f; + float RippleBreadth = .5f; + float RippleLifetime = .5f; + }; + + struct alignas(16) PerFrame + { + REX::W32::XMFLOAT4X4 OcclusionViewProj; + float Time; + float Raining; + float Wetness; + float PuddleWetness; + Settings settings; + uint pad0; + }; + STATIC_ASSERT_ALIGNAS_16(PerFrame); + + struct DebugSettings + { + bool EnableWetnessOverride = false; + bool EnablePuddleOverride = false; + bool EnableRainOverride = false; + bool EnableIntExOverride = false; + float2 WetnessOverride = float2(0.0f, 0.0f); + float2 PuddleWetnessOverride = float2(0.0f, 0.0f); + float2 RainOverride = float2(0.0f, 0.0f); + } debugSettings; + + Settings settings; + // Climate preset system + enum class ClimatePreset : uint32_t + { + Custom = 0, + Legacy = 1, + NordicStandard = 2, + ArcticTundra = 3, + TemperateCoastal = 4, + MonsoonExtreme = 5 + }; + struct ClimateSettings + { + float wetnessMultiplier; + float puddleMultiplier; + float transitionSpeed; + float raindropChance; + float raindropGridSize; + float raindropInterval; + }; + static constexpr ClimatePreset defaultPreset = ClimatePreset::NordicStandard; + ClimatePreset climatePreset = defaultPreset; + + PerFrame GetCommonBufferData() const; + + virtual void Prepass() override; + virtual void PostPostLoad() override; + + virtual void DrawSettings() override; + + virtual void LoadSettings(json& o_json) override; + virtual void SaveSettings(json& o_json) override; + + virtual void RestoreDefaultSettings() override; + + virtual bool SupportsVR() override { return true; }; + + // Override to provide weather analysis configuration + virtual WeatherAnalysisConfig GetWeatherAnalysisConfig() const override + { + return WeatherAnalysisConfig("Rain & Wetness Analysis", [this]() { + this->DrawWeatherAnalysis(); + }); + } + + // Constants and utilities for rain intensity calculations + static constexpr float MAX_RAIN_PARTICLE_DENSITY = 3.0f; + + // Helper function to extract rain intensity from precipitation object and weather + static float GetRainIntensity(RE::NiPointer precipObject, RE::TESWeather* weather); // Helper function to calculate precipitation rate from shader data and settings + float CalculatePrecipitationRate(float raindropChance, float raindropGridSizeGameUnits, float raindropIntervalSeconds, float mlPerDrop = 0.01f) const; + static const ClimateSettings& GetClimateSettings(ClimatePreset preset); + void ApplyClimatePreset(ClimatePreset preset); + bool DoesCurrentSettingsMatchPreset(ClimatePreset preset) const; + void DetectCurrentPreset(); + +private: + void DrawWeatherAnalysis() const; + + bool splashesOfStormsLoaded = false; + + // Weather wetness calculation result for debug display + struct WeatherWetnessResult + { + float wetness = 0.0f; + float puddleWetness = 0.0f; + }; + + WeatherWetnessResult CalculateWeatherWetness(RE::TESWeather* weather, float weatherPct, bool isCurrentWeather) const; +}; diff --git a/tools/feature_version_audit.py b/tools/feature_version_audit.py index feca06dfd6..d8ff992b4b 100644 --- a/tools/feature_version_audit.py +++ b/tools/feature_version_audit.py @@ -22,9 +22,8 @@ DEFAULT_SHADER_TYPES = (".ini", ".hlsl", ".hlsli") # Regex patterns for feature metadata extraction (all DRY, only here) -RE_MOD_ID = re.compile(r'MOD_ID\s*=\s*"([^"]+)"') -RE_FEATURE_MOD_LINK_DIRECT = re.compile(r'GetFeatureModLink\s*\([^)]*\)\s*\{\s*return\s*"(https?://[^"]+)";\s*\}') -RE_FEATURE_MOD_LINK_NEXUS = re.compile(r'GetFeatureModLink\s*\([^)]*\)\s*\{\s*return\s*MakeNexusModURL\(MOD_ID\);') +RE_NEXUS_MOD_ID_INI = re.compile(r'^NexusModID\s*=\s*(.+)$', re.MULTILINE) +RE_SHORT_NAME = re.compile(r'GetShortName\s*\([^)]*\)\s*(?:override)?\s*\{\s*return\s*"([^"]+)"') RE_FEATURE_SUMMARY_DIRECT = re.compile(r'GetFeatureSummary\s*\([^)]*\)\s*(?:override)?\s*\{\s*return \{\s*"([^"]+)"\s*,\s*\{([^}]*)\}', re.DOTALL) RE_FEATURE_SUMMARY_MULTILINE = re.compile(r'GetFeatureSummary\s*\([^)]*\)\s*(?:override)?\s*\{\s*return \{\s*((?:"[^"]*"\s*)+),\s*\{([^}]*)\}', re.DOTALL) RE_FEATURE_SUMMARY_CPP = re.compile(r'GetFeatureSummary\s*\([^)]*\)\s*\{[^}]*?std::string description\s*=\s*"([^"]+)";\s*std::vector keyFeatures\s*=\s*\{([^}]*)\}', re.DOTALL) @@ -174,20 +173,11 @@ def apply_version_bump(ini_path, proposed_ver_str): return False def parse_feature_metadata_file(path, mod_id=None, is_core=False): - mod_link = "" + mod_link = DEFAULT_NEXUS_BASE_URL + mod_id if (mod_id and not is_core) else "" description = "" key_features = [] with open(path, encoding="utf-8") as f: content = f.read() - # modId - if not mod_id: - mod_id = extract_regex(RE_MOD_ID, content) - # GetFeatureModLink - mod_link = extract_regex(RE_FEATURE_MOD_LINK_DIRECT, content) or mod_link - if RE_FEATURE_MOD_LINK_NEXUS.search(content) and mod_id: - mod_link = DEFAULT_NEXUS_BASE_URL + mod_id - if not mod_link and not is_core and mod_id: - mod_link = DEFAULT_NEXUS_BASE_URL + mod_id # GetFeatureSummary m = RE_FEATURE_SUMMARY_DIRECT.search(content) if m: @@ -228,9 +218,20 @@ def extract_feature_metadata(feature_headers_dir): # IsCore if RE_IS_CORE.search(content): is_core = True - m = RE_MOD_ID.search(content) + # Short name (may differ from header stem, e.g. IBL -> ImageBasedLighting) + m_sn = RE_SHORT_NAME.search(content) + if m_sn: + short_name = m_sn.group(1) + # --- Read NexusModID from feature ini (dirs may have spaces, search by ini filename) --- + ini_lookup_name = short_name if short_name else name + found_inis = list(DEFAULT_FEATURES_DIR.glob(f"*/Shaders/Features/{ini_lookup_name}.ini")) + if found_inis: + ini_content = found_inis[0].read_text(encoding="utf-8") + m = RE_NEXUS_MOD_ID_INI.search(ini_content) if m: - mod_id = m.group(1) + val = m.group(1).strip() + if val: + mod_id = val h_meta = parse_feature_metadata_file(header, mod_id=mod_id, is_core=is_core) # --- If missing, try .cpp --- cpp_path = header.with_suffix('.cpp')