From d51a16181a74124dcedd1c2e69c3376b11440e79 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Mon, 8 Jun 2026 20:58:30 -0400 Subject: Shadow res parameter, fix param combo logic Signed-off-by: Andrew Opalach --- Plugin.cs | 304 ++++++++++++++++++++++++++++++++------------------------------ 1 file changed, 156 insertions(+), 148 deletions(-) diff --git a/Plugin.cs b/Plugin.cs index 387af13..b01b705 100755 --- a/Plugin.cs +++ b/Plugin.cs @@ -16,7 +16,6 @@ using SharpPluginLoader.Core.Memory; using SharpPluginLoader.Core.Entities; // @TODO: -// - Shadow resolution param. // - Hair clipping while looking down. // - Optimized update logic. // - Only update non-override values when ui is shown. @@ -116,6 +115,11 @@ namespace WorldTuningTool return ConfigManager.GetConfig(Instance!); } + static void saveConfig() + { + ConfigManager.SaveConfig(Instance!); + } + private const float defaultParamWidth = 0.215f; private static bool ignoreMinMax = false; @@ -177,7 +181,7 @@ namespace WorldTuningTool private static bool selectedOverrideSuperseded(Config config, List? globalOverrides, List? stageOverrides, Parameter? param) { - bool superseded = false; + bool superseded = globalOverrides != null && selectedStage != currentStage; if (param != null) { superseded |= globalOverrides == null && stageOverrides != null && @@ -244,6 +248,11 @@ namespace WorldTuningTool valueV.X = MemoryUtil.Read(offset); pendingUpdate = false; } + else if (Type == ParameterType.SHADOW_RESOLUTION) + { + valueInt = 1; + pendingUpdate = false; + } PerFrame = perFrame; NoOverride = noOverride; } @@ -263,9 +272,28 @@ namespace WorldTuningTool return mask; } - private void writeCurrentValue() + private void maybeWriteCurrentValue() { - if (lastAddr == 0x0) return; + switch (Type) + { + case ParameterType.PATCH_FLOAT: + new Patch(offset, BitConverter.GetBytes(valueV.X)).Enable(); + break; + case ParameterType.SHADOW_RESOLUTION: + if (valueInt > 1) + { + shadowResEnable(valueInt); + } + else + { + shadowResDisable(); + } + break; + } + if (PerFrame || lastAddr == 0x0) + { + return; + } switch (Type) { case ParameterType.BOOL: @@ -290,9 +318,6 @@ namespace WorldTuningTool case ParameterType.FLOAT: MemoryUtil.GetRef(lastAddr + offset) = valueV.X; break; - case ParameterType.PATCH_FLOAT: - new Patch(offset, BitConverter.GetBytes(valueV.X)).Enable(); - break; case ParameterType.VECTOR3: MemoryUtil.GetRef(lastAddr + offset) = new Vector3(valueV.X, valueV.Y, valueV.Z); break; @@ -327,10 +352,7 @@ namespace WorldTuningTool { oValueV = valueV; oValueInt = valueInt; - if (!PerFrame) - { - writeCurrentValue(); - } + maybeWriteCurrentValue(); overrideValue = true; } } @@ -364,10 +386,7 @@ namespace WorldTuningTool valueV = oValueV; valueInt = oValueInt; bValue = false; - if (!PerFrame) - { - writeCurrentValue(); - } + maybeWriteCurrentValue(); overrideValue = false; } } @@ -386,10 +405,7 @@ namespace WorldTuningTool { valueV = v4; valueInt = i1; - if (!PerFrame) - { - writeCurrentValue(); - } + maybeWriteCurrentValue(); } } @@ -535,10 +551,7 @@ namespace WorldTuningTool { valueV = qValueV; valueInt = qValueInt; - if (!PerFrame) - { - writeCurrentValue(); - } + maybeWriteCurrentValue(); queuedValue = false; } queuedOverride = false; @@ -575,6 +588,7 @@ namespace WorldTuningTool } case ParameterType.INT: case ParameterType.HEX: + case ParameterType.SHADOW_RESOLUTION: { ImGui.SetNextItemWidth(width * defaultParamWidth); ImGuiInputTextFlags flags = ImGuiInputTextFlags.EnterReturnsTrue; @@ -632,27 +646,34 @@ namespace WorldTuningTool break; } } - if (valueChanged && !ignoreMinMax) + if (valueChanged) { - switch (Type) + if (Type == ParameterType.SHADOW_RESOLUTION) { - case ParameterType.BOOL: - case ParameterType.FLAG: - case ParameterType.BYTE: - case ParameterType.INT: - case ParameterType.HEX: - i1 = Math.Clamp(i1, min, max); - break; - case ParameterType.FLOAT: - case ParameterType.PATCH_FLOAT: - case ParameterType.VECTOR3: - case ParameterType.VECTOR4: - v4 = new Vector4( - Math.Clamp(v4.X, minf, maxf), - Math.Clamp(v4.Y, minf, maxf), - Math.Clamp(v4.Z, minf, maxf), - Math.Clamp(v4.W, minf, maxf)); - break; + i1 = Math.Clamp(i1, 1, 5); // Hard limit. + } + else if (!ignoreMinMax) + { + switch (Type) + { + case ParameterType.BOOL: + case ParameterType.FLAG: + case ParameterType.BYTE: + case ParameterType.INT: + case ParameterType.HEX: + i1 = Math.Clamp(i1, min, max); + break; + case ParameterType.FLOAT: + case ParameterType.PATCH_FLOAT: + case ParameterType.VECTOR3: + case ParameterType.VECTOR4: + v4 = new Vector4( + Math.Clamp(v4.X, minf, maxf), + Math.Clamp(v4.Y, minf, maxf), + Math.Clamp(v4.Z, minf, maxf), + Math.Clamp(v4.W, minf, maxf)); + break; + } } } return valueChanged; @@ -673,6 +694,7 @@ namespace WorldTuningTool case ParameterType.INT: case ParameterType.HEX: case ParameterType.COLOR: + case ParameterType.SHADOW_RESOLUTION: if (Type == ParameterType.INT) { ImGui.Text($"({name}: {i1})"); @@ -750,9 +772,9 @@ namespace WorldTuningTool { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); } - if (DrawValue(ref valueV, ref valueInt, width, !bValue) && !PerFrame) + if (DrawValue(ref valueV, ref valueInt, width, !bValue)) { - writeCurrentValue(); + maybeWriteCurrentValue(); } if (!overrideValue) { @@ -778,10 +800,7 @@ namespace WorldTuningTool { (bValueV, valueV) = (valueV, bValueV); (bValueInt, valueInt) = (valueInt, bValueInt); - if (!PerFrame) - { - writeCurrentValue(); - } + maybeWriteCurrentValue(); } ImGui.SameLine(); DrawValue(ref bValueV, ref bValueInt, width, true); @@ -813,7 +832,10 @@ namespace WorldTuningTool { if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, this))) { - unsetIfOverridesContainsParam(config.Overrides[globalStage], this); + if (globalOverrides != null) + { + unsetIfOverridesContainsParam(globalOverrides, this); + } } ov.Set(); } @@ -957,6 +979,7 @@ namespace WorldTuningTool case ParameterType.BYTE: return sValueInt.ToString(); case ParameterType.INT: + case ParameterType.SHADOW_RESOLUTION: return sValueInt.ToString(); case ParameterType.HEX: case ParameterType.COLOR: @@ -988,6 +1011,7 @@ namespace WorldTuningTool valueInt = Convert.ToInt32(value); break; case ParameterType.INT: + case ParameterType.SHADOW_RESOLUTION: valueInt = Convert.ToInt32(value); break; case ParameterType.HEX: @@ -1060,14 +1084,15 @@ namespace WorldTuningTool } } - public void Draw(List? currentOverrides, List? stageOverrides, List? globalOverrides, bool superseded, float width) + public void Draw(List? currentOverrides, List? stageOverrides, List? globalOverrides, bool superseded, bool preset, float width) { - if (superseded) // superseded == !isSet. + bool fade = superseded; + if (fade) { Assert(!isSet); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } - if (currentOverrides == null || superseded) + if (currentOverrides == null) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); } @@ -1097,20 +1122,37 @@ namespace WorldTuningTool } } } - if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, iterParam))) + } + Param = iterParam; + (valueV, valueInt) = Param.GetValue(); + Save(); + if (preset) + { + superseded = overridesContainsParam(externalOverrides.Values, Param); + if (!superseded && wasSet) { - if (globalOverrides != null) + if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, Param))) { - unsetIfOverridesContainsParam(globalOverrides, iterParam); + if (globalOverrides != null) + { + unsetIfOverridesContainsParam(globalOverrides, Param); + } } + Set(); } } - Param = iterParam; - (valueV, valueInt) = Param.GetValue(); - Save(); - if (wasSet) // If this override is superseded, expect this Combo to be disabled. + else { - Set(); + Config config = getConfig(); + superseded = selectedOverrideSuperseded(config, globalOverrides, stageOverrides, Param); + if (!superseded && wasSet) + { + if (globalOverrides != null) + { + unsetIfOverridesContainsParam(globalOverrides, Param); + } + Set(); + } } } if (isSelected) ImGui.SetItemDefaultFocus(); @@ -1119,7 +1161,7 @@ namespace WorldTuningTool ImGui.EndCombo(); } ImGui.PopItemWidth(); - if (currentOverrides == null || superseded) + if (currentOverrides == null) { ImGui.PopItemFlag(); } @@ -1148,7 +1190,7 @@ namespace WorldTuningTool } } } - if (superseded) + if (fade) { ImGui.PopStyleVar(); } @@ -1170,7 +1212,7 @@ namespace WorldTuningTool Param.DrawReferenceValue("Current", v4, i1); } } - else if (superseded) + else if (fade) { ImGui.PopStyleVar(); } @@ -1229,7 +1271,7 @@ namespace WorldTuningTool newParameter("Broad Area Shadow Enable", 0x5534, ParameterType.BOOL), newParameter("Broad Area Shadow Center", 0x5540 + 0x40, ParameterType.VECTOR3), newParameter("Broad Area Shadow Range", 0x5560 + 0x40, ParameterType.VECTOR3), - newParameter("Broad Area Shadow Direction", 0x5550 + 0x40, ParameterType.VECTOR3, pStep(0.001f)), + newParameter("Broad Area Shadow Direction", 0x5550 + 0x40, ParameterType.VECTOR3, pStep(0.00025f)), newParameter("Broad Area Shadow Max LOD Level", 0x5524, ParameterType.FLOAT), newParameter("Broad Area Shadow Culling Size", 0x5528, ParameterType.FLOAT), newParameter("Broad Area Shadow Precision", 0x5518, ParameterType.INT, pMinMaxStep(0, 2, 1)) @@ -1301,17 +1343,18 @@ namespace WorldTuningTool newParameter("SSLR Intensity", 0xB424, ParameterType.FLOAT) }; - // This is actually an InfiniteLight object. + private static Parameter shadowResParameter = newParameter("Shadow Resolution Multiplier", 0, ParameterType.SHADOW_RESOLUTION, pStep(1)); private delegate void UpdateShadowParams(nint shadowParams, nint stackOffset); private delegate void StaticShadowParams(nint shadowParamsStatic, nint shadowObjectInternal); private Hook? updateShadowParams; private Hook? staticShadowParams; private static Parameter[] shadowParameters1 = { }; + // This is actually an InfiniteLight object. // Non-PerFrame parameters will be written directly to the light object. private static Parameter[] shadowParameters2 = { newFrameParameter("Sunlight Group", 0x14, ParameterType.INT, pMinMaxStep(1, 255, 1)), - newFrameParameter("Sunlight Direction", 0x110, ParameterType.VECTOR3, pStep(0.001f)), + newFrameParameter("Sunlight Direction", 0x110, ParameterType.VECTOR3, pStep(0.00025f)), newFrameParameter("Sunlight Color", 0xE0, ParameterType.FLOAT), newFrameParameter("Sunlight Intensity", 0xF4, ParameterType.FLOAT), newParameter("Sunlight Min Roughness", 0x58C, ParameterType.FLOAT, pStep(0.0025f)), // +0xFC in params but is never read. @@ -1429,9 +1472,9 @@ namespace WorldTuningTool newParameter("Dof Aspect", 0x1D8, ParameterType.FLOAT, pMinStep(0.0f, 0.001f)), newFrameParameter("Vignetting Enabled", 0x200, ParameterType.BOOL), newFrameParameter("Vignetting Ellipse", 0x201, ParameterType.BOOL), - newFrameParameter("Vignetting Ellipticity", 0x204, ParameterType.FLOAT), - newFrameParameter("Vignetting Offset", 0x1F8, ParameterType.FLOAT), - newFrameParameter("Vignetting Pow", 0x1FC, ParameterType.FLOAT), + newFrameParameter("Vignetting Ellipticity", 0x204, ParameterType.FLOAT, pStep(0.01f)), + newFrameParameter("Vignetting Offset", 0x1F8, ParameterType.FLOAT, pStep(0.001f)), + newFrameParameter("Vignetting Pow", 0x1FC, ParameterType.FLOAT, pStep(0.01f)), newFrameParameter("Vignetting Color", 0x208, ParameterType.COLOR) }; @@ -1470,6 +1513,7 @@ namespace WorldTuningTool private static Parameter[] allParameters = new Parameter[]{ hqMode } .Concat(lodParameters) .Concat(snowParameters) + .Concat(new Parameter[] { shadowResParameter }) .Concat(shadowParameters2) .Concat(shadowCascadeParameters) .Concat(broadAreaShadowParameters) @@ -1484,22 +1528,27 @@ namespace WorldTuningTool .Concat(motionBlurParameters) .Concat(fxaaParameters).ToArray(); - private int shadowResMultiplier = 1; - private NativeAction setShadowQuality; - private Patch shadowRes1_2x; - private Patch shadowRes1_3x; - private Patch shadowRes1_4x; - private Patch shadowRes1_5x; - private Patch shadowRes1_1; - private Patch shadowRes1_2; - private Patch shadowRes1_3; - private Patch shadowRes1_4; - private Patch shadowRes2; - private Patch shadowRes3; + private static int currentShadowResMultiplier = 1; + private static NativeAction setShadowQuality; + private static Patch shadowRes1_2x; + private static Patch shadowRes1_3x; + private static Patch shadowRes1_4x; + private static Patch shadowRes1_5x; + private static Patch shadowRes1_1; + private static Patch shadowRes1_2; + private static Patch shadowRes1_3; + private static Patch shadowRes1_4; + private static Patch shadowRes2; + private static Patch shadowRes3; - private void shadowResEnable(int prevMultiplier) + private static void shadowResEnable(int multiplier) { - switch (prevMultiplier) + if (multiplier == currentShadowResMultiplier) + { + return; + } + Assert(multiplier != 1); + switch (currentShadowResMultiplier) { case 1: shadowRes1_1.Enable(); @@ -1522,7 +1571,7 @@ namespace WorldTuningTool shadowRes1_5x.Disable(); break; } - switch (shadowResMultiplier) + switch (multiplier) { case 2: shadowRes1_2x.Enable(); @@ -1538,12 +1587,17 @@ namespace WorldTuningTool shadowRes1_5x.Enable(); break; } + currentShadowResMultiplier = multiplier; + setShadowQuality.Invoke(MemoryUtil.Read(sMhScene!.Instance + 0x5530) + 1); } - private void shadowResDisable(int prevMultiplier) + private static void shadowResDisable() { - Assert(shadowResMultiplier == 1 && prevMultiplier != 1); - switch (prevMultiplier) + if (currentShadowResMultiplier == 1) + { + return; + } + switch (currentShadowResMultiplier) { case 2: shadowRes1_2x.Disable(); @@ -1564,6 +1618,8 @@ namespace WorldTuningTool shadowRes1_4.Disable(); shadowRes2.Disable(); shadowRes3.Disable(); + currentShadowResMultiplier = 1; + setShadowQuality.Invoke(MemoryUtil.Read(sMhScene!.Instance + 0x5530) + 1); } private bool fullResVolumeBlur = false; @@ -1656,25 +1712,7 @@ namespace WorldTuningTool newFrameParameter("Shadow Depth Bias", 0x57C, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Shadow Sloped Depth Bias", 0x580, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Shadow Max Depth Bias", 0x584, ParameterType.FLOAT, pStep(0.00001f)), - //new Parameter("Use Umbra", 0x591, ParameterType.BOOL, true), - //new Parameter("Enable Camera LOD", 0x592, ParameterType.BOOL, true), - //new Parameter("Llk Intensity", 0x974, ParameterType.FLOAT, true), - //new Parameter("Llk Radius", 0x978, ParameterType.FLOAT, true), - //new Parameter("Llk Effective Radius", 0x97C, ParameterType.FLOAT, true), - //new Parameter("Cone", 0x5F0, ParameterType.FLOAT, true, 0.1f), - //new Parameter("Spread", 0x5F4, ParameterType.FLOAT, true, 0.1f) - }; - /* - public Parameter[] RandomMoveParameters = { - new Parameter("Random Move Radius", 0x8E8, ParameterType.FLOAT, true), - new Parameter("Random Move Force To Center", 0x8EC, ParameterType.FLOAT, true), - new Parameter("Random Move Time Min", 0x8F8, ParameterType.FLOAT, true), - new Parameter("Random Move Time Max", 0x8FC, ParameterType.FLOAT, true), - new Parameter("Random Move Friction", 0x900, ParameterType.FLOAT, true), - new Parameter("Random Move Speed V Max", 0x940, ParameterType.VECTOR3, true), - new Parameter("Random Move Force V Max", 0x960, ParameterType.VECTOR3, true) }; - */ public Light(nint lightObject, int type) { @@ -1836,6 +1874,7 @@ namespace WorldTuningTool addr = PatternScanner.FindFirst(Pattern.FromString("48 83 EC 28 F3 0F 10 0D ?? ?? ?? ?? 80 F9 04 75 2F 48 8B 0D ?? ?? ?? ?? E8 D3 8A E4 01 48 8B 0D ?? ?? ?? ?? 33 D2 E8 ?? ?? ?? ??")); Assert(addr == 0x14043FF80); + // @TODO: Allocate our own memory for this? Assert(MemoryUtil.Read(0x142EE1A90) == 2.0f); Assert(MemoryUtil.Read(0x142E4FE5C) == 3.0f); Assert(MemoryUtil.Read(0x142C4A2F0) == 4.0f); @@ -1987,11 +2026,6 @@ namespace WorldTuningTool { Config config = getConfig(); Config.PatchConfig patches = config.Patches; - shadowResMultiplier = patches.ShadowResolutionMultiplier; - if (shadowResMultiplier > 1) - { - shadowResEnable(1); - } fullResVolumeBlur = patches.FullResolutionVolumeBlur; if (fullResVolumeBlur) { @@ -2036,7 +2070,7 @@ namespace WorldTuningTool { ovG.Set(); } - ConfigManager.SaveConfig(this); + saveConfig(); } /* It's hard to test, but I'm pretty sure just updating stepTime isn't enough for a higher rate to look right. @@ -2608,10 +2642,6 @@ namespace WorldTuningTool if (ImGui.Button("Add override")) { Override ov = new Override(); - if (currentStage == selectedStage || selectedStage == globalStage) - { - ov.Set(); // Empty override never superseded. - } selectedOverrides.Add(ov); } ImGui.PopItemWidth(); @@ -2630,7 +2660,7 @@ namespace WorldTuningTool { (selectedOverrides[i], selectedOverrides[i + 1]) = (selectedOverrides[i + 1], selectedOverrides[i]); } - ovS.Draw(selectedOverrides, null, globalOverrides, superseded, width); + ovS.Draw(selectedOverrides, null, globalOverrides, superseded, false, width); if (requestUp && i > 0) { (selectedOverrides[i], selectedOverrides[i - 1]) = (selectedOverrides[i - 1], selectedOverrides[i]); @@ -2663,7 +2693,7 @@ namespace WorldTuningTool } ovS.Save(); } - ConfigManager.SaveConfig(this); + saveConfig(); } ImGui.Separator(); @@ -2821,7 +2851,7 @@ namespace WorldTuningTool { (presetOverrides[i], presetOverrides[i + 1]) = (presetOverrides[i + 1], presetOverrides[i]); } - ovP.Draw(presetOverrides, stageOverrides, globalOverrides, superseded, width); + ovP.Draw(presetOverrides, stageOverrides, globalOverrides, superseded, true, width); if (requestUp && i > 0) { (presetOverrides[i], presetOverrides[i - 1]) = (presetOverrides[i - 1], presetOverrides[i]); @@ -2857,7 +2887,7 @@ namespace WorldTuningTool } ovP.Save(); } - ConfigManager.SaveConfig(this); + saveConfig(); } } ImGui.PopID(); @@ -2868,7 +2898,7 @@ namespace WorldTuningTool ImGui.Text("External:"); foreach (Override ovE in externalOverrides.Values) { - ovE.Draw(null, null, null, false, width); + ovE.Draw(null, null, null, false, false, width); } ImGui.PopID(); @@ -2877,29 +2907,6 @@ namespace WorldTuningTool if (ImGui.CollapsingHeader("Patches")) { Config.PatchConfig patches = config.Patches; - int prevShadowMultiplier = shadowResMultiplier; - ImGui.SetNextItemWidth(width * 0.2f); - if (ImGui.InputInt("Shadow Resolution Multiplier", ref shadowResMultiplier, 1)) - { - shadowResMultiplier = Math.Clamp(shadowResMultiplier, 1, 5); - if (shadowResMultiplier > 1) - { - shadowResEnable(prevShadowMultiplier); - } - else - { - shadowResDisable(prevShadowMultiplier); - } - setShadowQuality.Invoke(MemoryUtil.Read(sMhScene!.Instance + 0x5530) + 1); - patches.ShadowResolutionMultiplier = shadowResMultiplier; - config.Patches = patches; - ConfigManager.SaveConfig(this); - } - if (ImGui.BeginItemTooltip()) - { - ImGui.Text("In-Game Settings:\n - High: 1.0\n - Mid: 0.7\n - Low: 0.5"); - ImGui.EndTooltip(); - } if (ImGui.Checkbox("Volume Rendering Full Resolution Blur Pass (Requires Area Change)", ref fullResVolumeBlur)) { @@ -2913,7 +2920,7 @@ namespace WorldTuningTool } patches.FullResolutionVolumeBlur = fullResVolumeBlur; config.Patches = patches; - ConfigManager.SaveConfig(this); + saveConfig(); } if (ImGui.BeginItemTooltip()) { @@ -2933,7 +2940,7 @@ namespace WorldTuningTool } patches.HigherThanHighestVolumeRendering = higherVolumeQuality; config.Patches = patches; - ConfigManager.SaveConfig(this); + saveConfig(); } if (ImGui.BeginItemTooltip()) { @@ -2953,7 +2960,7 @@ namespace WorldTuningTool } patches.DisableLODLimits = disableLODLimits; config.Patches = patches; - ConfigManager.SaveConfig(this); + saveConfig(); } if (ImGui.BeginItemTooltip()) { @@ -2973,7 +2980,7 @@ namespace WorldTuningTool } patches.LargerFoliageSwayRange = largerFoliageSwayRange; config.Patches = patches; - ConfigManager.SaveConfig(this); + saveConfig(); } if (ImGui.BeginItemTooltip()) { @@ -2993,7 +3000,7 @@ namespace WorldTuningTool } patches.DisableReducedRateAnimations = disableReducedRateAnimations; config.Patches = patches; - ConfigManager.SaveConfig(this); + saveConfig(); } if (ImGui.BeginItemTooltip()) { @@ -3115,6 +3122,7 @@ namespace WorldTuningTool ImGui.PushID("Shadows"); if (ImGui.CollapsingHeader("Sunlight and Shadows")) { + shadowResParameter.Draw(width); ImGui.Text($"shadowObject: 0x{shadowAddr:X}, shadowAddr: 0x{shadowObject:X}"); /* if (ImGui.CollapsingHeader("Pass 1")) -- cgit v1.2.3-101-g0448