diff --git a/src/games/nightsofazure1/addon.cpp b/src/games/nightsofazure1/addon.cpp new file mode 100644 index 00000000..1bf2c632 --- /dev/null +++ b/src/games/nightsofazure1/addon.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2023 Carlos Lopez + * SPDX-License-Identifier: MIT + */ + +#define ImTextureID ImU64 + +#define DEBUG_LEVEL_0 + +//#define DEBUG_LEVEL_1 //added + +#include // UI -- Main +#include // videos -- pre-renderd movies +#include // Tonemap +#include // Final + + + + +#include +#include + +#include "../../mods/shader.hpp" +#include "../../mods/swapchain.hpp" +#include "../../utils/settings.hpp" +#include "./shared.h" + +namespace { + +renodx::mods::shader::CustomShaders custom_shaders = { + + CustomShaderEntry(0xE53D74F1), // UI -- Main + CustomShaderEntry(0x5D15CFEE), // videos -- pre-rendered movies + CustomShaderEntry(0xA531D673), // Tonemap + CustomShaderEntry(0xBB055A34), // Final + + +}; + +ShaderInjectData shader_injection; + +renodx::utils::settings::Settings settings = { + new renodx::utils::settings::Setting{ + .key = "toneMapType", + .binding = &shader_injection.toneMapType, + .value_type = renodx::utils::settings::SettingValueType::INTEGER, + .default_value = 2.f, + .can_reset = false, + .label = "Tone Mapper", + .section = "Tone Mapping", + .tooltip = "Sets the tone mapper type", + .labels = {"Vanilla", "None", "DICE"}, + }, + new renodx::utils::settings::Setting{ + .key = "toneMapPeakNits", + .binding = &shader_injection.toneMapPeakNits, + .default_value = 1000.f, + .can_reset = false, + .label = "Peak Brightness", + .section = "Tone Mapping", + .tooltip = "Sets the value of peak white in nits", + .min = 48.f, + .max = 4000.f, + }, + new renodx::utils::settings::Setting{ + .key = "toneMapGameNits", + .binding = &shader_injection.toneMapGameNits, + .default_value = 203.f, + .label = "Game Brightness", + .section = "Tone Mapping", + .tooltip = "Sets the value of 100%% white in nits", + .min = 48.f, + .max = 500.f, + }, + new renodx::utils::settings::Setting{ + .key = "toneMapUINits", + .binding = &shader_injection.toneMapUINits, + .default_value = 203.f, + .label = "UI Brightness", + .section = "Tone Mapping", + .tooltip = "Sets the brightness of UI and HUD elements in nits", + .min = 48.f, + .max = 500.f, + }, + + + +}; + +void OnPresetOff() { + renodx::utils::settings::UpdateSetting("toneMapType", 0.f); + renodx::utils::settings::UpdateSetting("toneMapPeakNits", 203.f); + renodx::utils::settings::UpdateSetting("toneMapGameNits", 203.f); + renodx::utils::settings::UpdateSetting("toneMapUINits", 203.f); + +} + +} // namespace + +// NOLINTBEGIN(readability-identifier-naming) + +extern "C" __declspec(dllexport) const char* NAME = "RenoDX"; +extern "C" __declspec(dllexport) const char* DESCRIPTION = "RenoDX for Knights of Azure 1"; + +// NOLINTEND(readability-identifier-naming) + +BOOL APIENTRY DllMain(HMODULE h_module, DWORD fdw_reason, LPVOID lpv_reserved) { + switch (fdw_reason) { + case DLL_PROCESS_ATTACH: + if (!reshade::register_addon(h_module)) return FALSE; + renodx::mods::shader::force_pipeline_cloning = true; //So the mod works with the toolkit + + renodx::mods::swapchain::force_borderless = false; //needed for stability + renodx::mods::swapchain::prevent_full_screen = false; //needed for stability + + + // BGRA8_typeless needed to uncap luminance + + // BGRA8_typeless + renodx::mods::swapchain::swap_chain_upgrade_targets.push_back({ + .old_format = reshade::api::format::b8g8r8a8_typeless, + .new_format = reshade::api::format::r16g16b16a16_float, + // .index = 39, //Maybe find the specific render target that uncaps the game one day, but not right now + // .ignore_size = true, //Ignoring size allows you to uncap when the game runs in a sub-native resolution, but tons of artifacts are created + }); + + + // BGRA8_unorm + // renodx::mods::swapchain::swap_chain_upgrade_targets.push_back({ + // .old_format = reshade::api::format::b8g8r8a8_unorm, + // .new_format = reshade::api::format::r16g16b16a16_float, + // .index = 39, + //.ignore_size = true, + // }); + + + + break; + case DLL_PROCESS_DETACH: + reshade::unregister_addon(h_module); + break; + } + + renodx::utils::settings::Use(fdw_reason, &settings, &OnPresetOff); + + renodx::mods::swapchain::Use(fdw_reason); + + renodx::mods::shader::Use(fdw_reason, custom_shaders, &shader_injection); + + return TRUE; +} diff --git a/src/games/nightsofazure1/final_0xBB055A34.ps_5_0.hlsl b/src/games/nightsofazure1/final_0xBB055A34.ps_5_0.hlsl new file mode 100644 index 00000000..95e40eab --- /dev/null +++ b/src/games/nightsofazure1/final_0xBB055A34.ps_5_0.hlsl @@ -0,0 +1,30 @@ +// ---- Created with 3Dmigoto v1.3.16 on Wed Jul 31 20:18:32 2024 +// Final shader in the game's shader order +// simple texture sample that scales up the entire game's brightness +// We will be moving paper white + handle the UI here + +#include "./shared.h" + +SamplerState smplScene_s : register(s0); +Texture2D smplScene_Tex : register(t0); + + +// 3Dmigoto declarations +#define cmp - + + +void main( + float4 v0 : SV_Position0, + float2 v1 : TEXCOORD0, + out float4 o0 : SV_Target0) +{ + o0.xyzw = smplScene_Tex.Sample(smplScene_s, v1.xy).xyzw; + + + o0.rgb = renodx::math::SafePow(o0.rgb, 2.2f); + o0.rgb *= injectedData.toneMapGameNits; + o0.rgb /= 80.f; + + + return; +} \ No newline at end of file diff --git a/src/games/nightsofazure1/shared.h b/src/games/nightsofazure1/shared.h new file mode 100644 index 00000000..7df04c4b --- /dev/null +++ b/src/games/nightsofazure1/shared.h @@ -0,0 +1,23 @@ +#ifndef KOAZURE1_SHARED_H_ +#define SRC_KOAZURE1_SHARED_H_ + +#ifndef __cplusplus +#include "../../shaders/renodx.hlsl" +#endif + +// Must be 32bit aligned +// Should be 4x32 +struct ShaderInjectData { + float toneMapType; + float toneMapPeakNits; + float toneMapGameNits; + float toneMapUINits; +}; + +#ifndef __cplusplus +cbuffer cb13 : register(b13) { + ShaderInjectData injectedData : packoffset(c0); // +} +#endif + +#endif // SRC_KOAZURE1_SHARED_H_ diff --git a/src/games/nightsofazure1/tonemap_0xA531D673.ps_5_0.hlsl b/src/games/nightsofazure1/tonemap_0xA531D673.ps_5_0.hlsl new file mode 100644 index 00000000..230c3858 --- /dev/null +++ b/src/games/nightsofazure1/tonemap_0xA531D673.ps_5_0.hlsl @@ -0,0 +1,81 @@ +// ---- Created with 3Dmigoto v1.3.16 on Sun Aug 11 19:29:56 2024 +// This is the last shader before the UI gets drawn +// The game has no tonemapper, so we're just going to slap on DICE here to compress the highlights, and call it a day +// With just BGR8_TYPELESS upgraded, the game runs bright, so we'll just add a slider, and hdr done! + +#include "./shared.h" + +SamplerState smplScene_s : register(s0); +SamplerState smplBlurFront_s : register(s1); +SamplerState smplBlurBack_s : register(s2); +Texture2D smplScene_Tex : register(t0); +Texture2D smplBlurFront_Tex : register(t1); +Texture2D smplBlurBack_Tex : register(t2); + + +// 3Dmigoto declarations +#define cmp - + + +void main( + float4 v0 : SV_Position0, + float2 v1 : TEXCOORD0, + out float4 o0 : SV_Target0) +{ + float4 r0,r1,r2; + uint4 bitmask, uiDest; + float4 fDest; + + r0.xyzw = smplBlurBack_Tex.Sample(smplBlurBack_s, v1.xy).xyzw; + r1.xyzw = smplScene_Tex.Sample(smplScene_s, v1.xy).xyzw; + r2.x = cmp(r1.w < 0.5); + r0.xyzw = r2.xxxx ? r1.xyzw : r0.xyzw; + r0.xyzw = r0.xyzw + -r1.xyzw; + r2.x = -0.5 + r1.w; + r2.x = abs(r2.x) * -2 + 1; + r2.x = max(0, r2.x); + r2.x = 9.99999975e-06 + r2.x; + r2.x = 1 / r2.x; + r2.x = -1 + r2.x; + r2.x = saturate(0.25 * r2.x); + r0.xyzw = r2.xxxx * r0.xyzw + r1.xyzw; + r1.xyzw = smplBlurFront_Tex.Sample(smplBlurFront_s, v1.xy).xyzw; + r2.xyzw = r1.xyzw + -r0.xyzw; + r1.x = -0.5 + r1.w; + r1.x = abs(r1.x) * -2 + 1; + r1.x = max(0, r1.x); + r1.x = 9.99999975e-06 + r1.x; + r1.x = 1 / r1.x; + r1.x = -1 + r1.x; + r1.x = saturate(0.25 * r1.x); + o0.xyzw = r1.xxxx * r2.xyzw + r0.xyzw; + //Vanilla shader end + + // Start dice (ty Musa for max pain example code) + if (injectedData.toneMapType >= 2) + { + //Converting from gamma space to linear space + const float paperWhite = injectedData.toneMapGameNits / 80.f; + float3 linearColor = pow(abs(o0.xyz), 2.2) * sign(o0.xyz); + linearColor *= paperWhite; + + const float peakWhite = injectedData.toneMapPeakNits / 80.f; //Getting the peak slider's value + const float highlightsShoulderStart = paperWhite; // Don't tonemap the "SDR" range + linearColor = renodx::tonemap::dice::BT709(linearColor, peakWhite, highlightsShoulderStart); //Do DICE with inputs + + linearColor /= paperWhite; + + float3 linearSDR = sign(r0.rgb) * pow(abs(r0.rgb), 2.2f); + + + o0.xyz = pow(abs(linearColor), 1.0 / 2.2) * sign(linearColor); //Inverse 2.2 gamma as the final output; Final will convert back to linear space + + + } + else if (injectedData.toneMapType == 0) //If tonemapper is vanilla, output is clamped + { + o0.xyz = saturate(o0.xyz); + } + + return; +} \ No newline at end of file diff --git a/src/games/nightsofazure1/tonemapper.hlsl b/src/games/nightsofazure1/tonemapper.hlsl new file mode 100644 index 00000000..981fd454 --- /dev/null +++ b/src/games/nightsofazure1/tonemapper.hlsl @@ -0,0 +1,64 @@ +// Custom Tonemapper +// We'll create a function so we can just call this in other shaders, instead of having to manage a wall of code in multiple files + +#include "./shared.h" + +float3 applyUserTonemap(float3 untonemapped, float3 vanillaColor, float midGray){ + float3 outputColor; + + if (injectedData.toneMapType == 0.f) + { + outputColor = vanillaColor; + outputColor = max(0, outputColor); //clamps to 709/no negative colors for the vanilla tonemapper + } + else + { + outputColor = untonemapped; + } + + + + //float vanillaMidGray = 0.1f; //0.18f old default + float vanillaMidGray = midGray; //calculate mid grey from the second hable run + float renoDRTContrast = 1.f; + float renoDRTFlare = 0.f; + float renoDRTShadows = 1.f; + //float renoDRTDechroma = 0.8f; + float renoDRTDechroma = injectedData.colorGradeBlowout; + float renoDRTSaturation = 1.f; // + float renoDRTHighlights = 1.f; + + renodx::tonemap::Config config = renodx::tonemap::config::Create( + injectedData.toneMapType, + injectedData.toneMapPeakNits, + injectedData.toneMapGameNits, + 1, + injectedData.colorGradeExposure, + injectedData.colorGradeHighlights, + injectedData.colorGradeShadows, + injectedData.colorGradeContrast, + injectedData.colorGradeSaturation, + vanillaMidGray, + vanillaMidGray * 100.f, + renoDRTHighlights, + renoDRTShadows, + renoDRTContrast, + renoDRTSaturation, + renoDRTDechroma, + renoDRTFlare); + + outputColor = renodx::tonemap::config::Apply(outputColor, config); + + + if (injectedData.toneMapType != 0) + { + + if (injectedData.blend) //HDR/SDR blend for color correction + { + outputColor = lerp(vanillaColor, outputColor, saturate(vanillaColor)); // combine tonemappers + } + + } + + return outputColor; +} \ No newline at end of file diff --git a/src/games/nightsofazure1/ui_0xE53D74F1.ps_5_0.hlsl b/src/games/nightsofazure1/ui_0xE53D74F1.ps_5_0.hlsl new file mode 100644 index 00000000..e90c2685 --- /dev/null +++ b/src/games/nightsofazure1/ui_0xE53D74F1.ps_5_0.hlsl @@ -0,0 +1,47 @@ +// ---- Created with 3Dmigoto v1.3.16 on Sun Aug 11 19:29:58 2024 +// Main UI Shader, gameplay, world, start menu + +#include "./shared.h" + +cbuffer _Globals : register(b0) +{ + float4 materialColor : packoffset(c0) = {1,1,1,1}; + bool maskFlag : packoffset(c1) = false; + bool ignoreVtxColorFlag : packoffset(c1.y) = false; + float HdrRangeInv : packoffset(c1.z) = {1}; +} + +SamplerState __smpsTex_s : register(s0); +Texture2D sTex : register(t0); + + +// 3Dmigoto declarations +#define cmp - + + +void main( + float4 v0 : TEXCOORD0, + float4 v1 : SV_Position0, + float2 v2 : TEXCOORD1, + out float4 o0 : SV_Target0) +{ + float4 r0,r1; + uint4 bitmask, uiDest; + float4 fDest; + + r0.x = 1; + r1.xyzw = sTex.Sample(__smpsTex_s, v2.xy).xyzw; + r0.w = r1.w; + r0.xyzw = maskFlag ? r0.xxxw : r1.xyzw; + r1.xyzw = ignoreVtxColorFlag ? float4(1,1,1,1) : v0.xyzw; + r0.xyzw = r1.xyzw * r0.xyzw; + r0.xyzw = materialColor.xyzw * r0.xyzw; + o0.xyz = HdrRangeInv * r0.xyz; + o0.w = r0.w; + + o0.rgb = renodx::math::SafePow(o0.rgb, 2.2f); // 2.2 gamma correction + o0.rgb *= injectedData.toneMapUINits / injectedData.toneMapGameNits; //Ratio of UI:Game brightness + o0.rgb = renodx::math::SafePow(o0.rgb, 1/2.2); //Inverse 2.2 gamma + + return; +} \ No newline at end of file diff --git a/src/games/nightsofazure1/videos_0x5D15CFEE.ps_4_0.hlsl b/src/games/nightsofazure1/videos_0x5D15CFEE.ps_4_0.hlsl new file mode 100644 index 00000000..e0e90db9 --- /dev/null +++ b/src/games/nightsofazure1/videos_0x5D15CFEE.ps_4_0.hlsl @@ -0,0 +1,38 @@ +// ---- Created with 3Dmigoto v1.3.16 on Tue Jul 23 02:31:46 2024 +#include "./shared.h" + +cbuffer _Globals : register(b0) +{ + float vATest : packoffset(c0); +} + +SamplerState smp_s : register(s0); +Texture2D tex : register(t0); + + +// 3Dmigoto declarations +#define cmp - + + +void main( + float4 v0 : SV_Position0, + float4 v1 : COLOR0, + float2 v2 : TEXCOORD0, + out float4 o0 : SV_Target0) +{ + float4 r0; + uint4 bitmask, uiDest; + float4 fDest; + + r0.x = -vATest + v1.w; + r0.x = cmp(r0.x < 0); + if (r0.x != 0) discard; + r0.xyzw = tex.Sample(smp_s, v2.xy).xyzw; + o0.xyz = v1.xyz * r0.xyz; + o0.w = v1.w; + + //o0.rgb = renodx::math::SafePow(o0.rgb, 2.2f); //2.2 gamma correction + //o0.rgb *= injectedData.toneMapGameNits / 80.f; //Using paper white saling, the movies are too bright for bt2446a + + return; +} \ No newline at end of file