From c691596de197ce3feec1217643b09783010fc415 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Tue, 16 Jun 2026 15:08:46 -0400 Subject: Optimize transitions, fix crash on new character Signed-off-by: Andrew Opalach --- Plugin.cs | 339 +++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 234 insertions(+), 105 deletions(-) (limited to 'Plugin.cs') diff --git a/Plugin.cs b/Plugin.cs index 47dce7e..93119c3 100755 --- a/Plugin.cs +++ b/Plugin.cs @@ -1,6 +1,7 @@ //#define ENABLE_ASSERTS //#define OVERSIZED_SHADOW_MAP //#define SHADER_FEATURES +//#define RESOURCE_ADJUSTMENT using System.Numerics; using System.Globalization; @@ -13,13 +14,14 @@ using SharpPluginLoader.Core; #if SHADER_FEATURES using SharpPluginLoader.Core.Rendering; #endif -//using SharpPluginLoader.Core.Resources; +#if RESOURCE_ADJUSTMENT +using SharpPluginLoader.Core.Resources; +#endif using SharpPluginLoader.Core.Configuration; using SharpPluginLoader.Core.Memory; using SharpPluginLoader.Core.Entities; // @TODO: -// - Hair clipping while looking down. // - Optimized update logic. // - Only update non-override values when ui is shown. // - Map sky params. @@ -37,6 +39,7 @@ using SharpPluginLoader.Core.Entities; // - Hotload nukes config? // // Known issues to think about: +// - Hair clipping while looking down. // - Volume rendering can look really bad with multiple overlapped sources(?) (Guding lands vines). // - Screen space reflections often look bad. // - Facial contact shadows incorrectly move based on camera position. @@ -133,23 +136,31 @@ namespace WorldTuningTool static void saveConfig() { ConfigManager.SaveConfig(Instance!); + selectedOrderChanged = false; + presetOrderChanged = false; } private const float defaultParamWidth = 0.215f; private static bool ignoreMinMax = false; - private const Stage globalStage = Stage.InfinityOfNothingHandler; - private static Stage currentStage => Area.CurrentStage; - private static Stage selectedStage = globalStage; - private static Stage previousStage = selectedStage; - + private const StageExt globalStage = StageExt.Global; + private static StageExt currentStage => (StageExt)Area.CurrentStage; private static List? maybeGetStageOverrides(Config config) { - return (currentStage != 0) ? config.Overrides[currentStage] : null; + if (!config.Overrides.ContainsKey(currentStage)) + { + return null; + } + return (currentStage != StageExt.Global) ? config.Overrides[currentStage] : null; } + private static StageExt selectedStage = globalStage; + private static StageExt previousStage = selectedStage; + private static bool selectedOrderChanged = false; + private static string selectedPreset = ""; + private static bool presetOrderChanged = false; private static bool renamingPreset = false; private static string typedPresetName = ""; @@ -236,6 +247,7 @@ namespace WorldTuningTool protected float stepf, minf, maxf; protected int step, min, max; + private bool pendingWrite = false; private Vector4 valueV = default; private int valueInt = 0; private Vector4 oValueV; // Original value. @@ -287,8 +299,25 @@ namespace WorldTuningTool return mask; } + public void Resolve() + { + Assert(!InTransition); + if (pendingWrite) + { + pendingWrite = false; + maybeWriteCurrentValue(); + } + } + private void maybeWriteCurrentValue() { + if (InTransition) + { + pendingWrite = true; + DirtyParams.Add(this); + return; + } + Assert(!pendingWrite); switch (Type) { case ParameterType.PATCH_FLOAT: @@ -752,8 +781,10 @@ namespace WorldTuningTool public void Draw(ref Vector4 v4, ref int i1, float width, bool assumeOverride) { ImGui.PushID(Name); + if (NoOverride) { + // Dependent on overall ImGui style. ImGui.SetCursorPos(ImGui.GetCursorPos() + new Vector2(40.0f, 0.0f)); } else if (!assumeOverride) @@ -794,6 +825,7 @@ namespace WorldTuningTool } ImGui.SameLine(); } + if (!overrideValue) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); @@ -806,7 +838,7 @@ namespace WorldTuningTool { ImGui.PopItemFlag(); } - Config config = getConfig(); + bool showButtons = !assumeOverride && overrideValue && !pendingUpdate; if (showButtons) { @@ -836,13 +868,12 @@ namespace WorldTuningTool bValue = false; } } + + Config config = getConfig(); List selectedOverrides = config.Overrides[selectedStage]; List? stageOverrides = maybeGetStageOverrides(config); + // Deciding when to set globalOverrides copies the logic in OnImGuiRender(). List? globalOverrides = null; - if (selectedStage != globalStage) - { - globalOverrides = config.Overrides[globalStage]; - } if (selectedPreset != "") { List presetOverrides = config.Presets[selectedPreset]; @@ -853,19 +884,20 @@ namespace WorldTuningTool { // Create override before unsetting global. Override ov = new Override(this, v4, i1); + globalOverrides = config.Overrides[globalStage]; bool superseded = overridesContainsParam(externalOverrides.Values, this); if (!superseded) { + BeginTransition(); if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, this))) { - if (globalOverrides != null) - { - unsetIfOverridesContainsParam(globalOverrides, this); - } + unsetIfOverridesContainsParam(globalOverrides, this); } ov.Set(); + EndTransition(); } presetOverrides.Add(ov); + presetOrderChanged = true; } if (ImGui.BeginItemTooltip()) { @@ -881,16 +913,23 @@ namespace WorldTuningTool { // Create override before unsetting global. Override ov = new Override(this, v4, i1); + if (selectedStage != globalStage) + { + globalOverrides = config.Overrides[globalStage]; + } bool superseded = selectedOverrideSuperseded(config, globalOverrides, stageOverrides, this); if (!superseded) { + BeginTransition(); if (globalOverrides != null) { unsetIfOverridesContainsParam(globalOverrides, this); } ov.Set(); + EndTransition(); } selectedOverrides.Add(ov); + selectedOrderChanged = true; } if (ImGui.BeginItemTooltip()) { @@ -898,9 +937,11 @@ namespace WorldTuningTool ImGui.EndTooltip(); } } + ImGui.SameLine(); DrawReferenceValue("Original", oValueV, oValueInt); } + ImGui.PopID(); } } @@ -1133,6 +1174,7 @@ namespace WorldTuningTool bool isSelected = Param == iterParam; if (ImGui.Selectable(iterParam.Name, isSelected)) { + BeginTransition(); bool wasSet = isSet; if (wasSet) { @@ -1179,6 +1221,7 @@ namespace WorldTuningTool Set(); } } + EndTransition(); } if (isSelected) ImGui.SetItemDefaultFocus(); } @@ -1255,6 +1298,24 @@ namespace WorldTuningTool private delegate void OnAreaChange(nint unknownPtr); private Hook? onAreaChange; + private static bool InTransition = false; + private static List DirtyParams = new List(); + private static void BeginTransition() + { + Assert(!InTransition && DirtyParams.Count == 0); + InTransition = true; + } + private static void EndTransition() + { + Assert(InTransition); + InTransition = false; + foreach (Parameter param in DirtyParams) + { + param.Resolve(); + } + DirtyParams.Clear(); + } + private static MtObject? sMhScene = null; private static Parameter hqMode = newParameter("HQ Mode", 0xE9A3, ParameterType.BOOL); @@ -1840,6 +1901,7 @@ namespace WorldTuningTool { Override ov = new Override(param, v4, i1); Config config = getConfig(); + BeginTransition(); if (!(selectedPreset != "" && unsetIfOverridesContainsParam(config.Presets[selectedPreset], param))) { List? stageOverrides = maybeGetStageOverrides(config); @@ -1849,6 +1911,7 @@ namespace WorldTuningTool } } ov.Set(); + EndTransition(); externalId++; externalOverrides.Add(externalId, ov); return externalId; @@ -1862,6 +1925,7 @@ namespace WorldTuningTool if (externalOverrides.ContainsKey(id)) { Override ovE = externalOverrides[id]; + BeginTransition(); ovE.Unset(); Config config = getConfig(); if (!(selectedPreset != "" && setIfOverridesContainsParam(config.Presets[selectedPreset], ovE.Param!))) @@ -1872,6 +1936,7 @@ namespace WorldTuningTool setIfOverridesContainsParam(config.Overrides[globalStage], ovE.Param!); } } + EndTransition(); externalOverrides.Remove(id); } } @@ -1886,6 +1951,13 @@ namespace WorldTuningTool Assert(Array.FindAll(allParameters, (p => p.Name == param.Name)).Length == 1); } + // Assert OrderedStages covers all values. + foreach (StageExt stage in Enum.GetValues(typeof(StageExt))) + { + if (stage == StageExt.Global) continue; + Assert(Config.OrderedStages.Contains(stage)); + } + onAreaChange = Hook.Create(0x141AC27D0, OnAreaChangeHook); // nint // Needed for at least "Broad Area Shadow Dir". Otherwise it will flicker during an update on center change. @@ -2121,7 +2193,11 @@ namespace WorldTuningTool sMhScene = SingletonManager.GetSingleton("sMhScene")!; sLightProbes = SingletonManager.GetSingleton("sLightProbes")!; Config config = getConfig(); - foreach (Stage area in Enum.GetValues(typeof(Stage))) + if (!config.Overrides.ContainsKey(globalStage)) + { + config.Overrides.Add(globalStage, new List()); + } + foreach (StageExt area in Config.OrderedStages) { if (!config.Overrides.ContainsKey(area)) { @@ -2132,13 +2208,21 @@ namespace WorldTuningTool { ovG.Set(); } + selectedPreset = config.SelectedPreset; + if (selectedPreset != "") + { + List globalOverrides = config.Overrides[globalStage]; + List? stageOverrides = maybeGetStageOverrides(config); + HandlePresetChange("", globalOverrides, stageOverrides); + } saveConfig(); } - /* It's hard to test, but I'm pretty sure just updating stepTime isn't enough for a higher rate to look right. + // It's hard to test, but I'm pretty sure just updating stepTime isn't enough for a higher rate to look right. // https://github.com/AsteriskAmpersand/CTC-MHW-Editor/blob/9ec6303042690478398801b1bb860a2d38298eee/structures/Ctc.py#L23 - private static bool adjustCtcParams = false; - private static float stepTimeFps = 60.0f; +#if RESOURCE_ADJUSTMENT + private bool adjustCtcParams = false; + private float stepTimeFps = 60.0f; public void OnResourceLoad(Resource? resource, MtDti dti, string path, LoadFlags flags) { if (adjustCtcParams && resource != null && resource.FileExtension == "ctc") @@ -2153,10 +2237,15 @@ namespace WorldTuningTool MemoryUtil.GetRef(resource.Instance + 0xC4) = 1.0f / stepTimeFps; } } - */ +#endif public void OnUpdate(float deltaTime) { + StageExt stage = currentStage; + if (stage == StageExt.Global && previousStage != StageExt.Global) + { + HandleAreaChange(stage); + } hqMode.Update(sMhScene!.Instance); foreach (Parameter param in lodParameters) { @@ -2613,35 +2702,77 @@ namespace WorldTuningTool } } - private void OnAreaChangeHook(nint unknownPtr) + private void HandlePresetChange(string prevPreset, List globalOverrides, List? stageOverrides) { - onAreaChange!.Original(unknownPtr); - Stage stage = currentStage; - if (stage == 0) stage = globalStage; - if (stage == previousStage) return; Config config = getConfig(); - List prevOverrides = config.Overrides[previousStage]; - previousStage = stage; - List? globalOverrides = null; - if (stage != globalStage) + BeginTransition(); + if (prevPreset != "") { - globalOverrides = config.Overrides[globalStage]; + List prevOverrides = config.Presets[prevPreset]; + foreach (Override ovR in prevOverrides) + { + bool superseded = ovR.Param != null && overridesContainsParam(externalOverrides.Values, ovR.Param); + if (!superseded) + { + ovR.Unset(); + if (ovR.Param != null) + { + if (!(stageOverrides != null && setIfOverridesContainsParam(stageOverrides, ovR.Param))) + { + setIfOverridesContainsParam(globalOverrides, ovR.Param); + } + } + } + } + } + if (selectedPreset != "") + { + List newOverrides = config.Presets[selectedPreset]; + foreach (Override ovN in newOverrides) + { + bool superseded = ovN.Param != null && overridesContainsParam(externalOverrides.Values, ovN.Param); + if (!superseded) + { + if (ovN.Param != null) + { + if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, ovN.Param))) + { + unsetIfOverridesContainsParam(globalOverrides, ovN.Param); + } + } + ovN.Set(); + } + } + } + EndTransition(); + } + + private void HandleAreaChange(StageExt stage) + { + Config config = getConfig(); + if (!config.Overrides.ContainsKey(stage)) + { + return; } - foreach (Override ovR in prevOverrides) + List prevOverrides = config.Overrides[previousStage]; + List globalOverrides = config.Overrides[globalStage]; + BeginTransition(); + if (previousStage != globalStage) { - // If previousStage was global, none of it's parameters could have been - // superseded by a non-global stage override. - bool superseded = stageOverrideSuperseded(config, ovR.Param); - if (!superseded) + foreach (Override ovR in prevOverrides) { - ovR.Unset(); - if (ovR.Param != null && globalOverrides != null) + bool superseded = stageOverrideSuperseded(config, ovR.Param); + if (!superseded) { - setIfOverridesContainsParam(globalOverrides, ovR.Param); + ovR.Unset(); + if (ovR.Param != null) + { + setIfOverridesContainsParam(globalOverrides, ovR.Param); + } } } } - if (globalOverrides != null) // Stage isn't global. + if (stage != globalStage) { List stageOverrides = config.Overrides[stage]; foreach (Override ovS in stageOverrides) @@ -2657,6 +2788,19 @@ namespace WorldTuningTool } } } + EndTransition(); + previousStage = stage; + } + + private void OnAreaChangeHook(nint unknownPtr) + { + onAreaChange!.Original(unknownPtr); + StageExt stage = currentStage; + if (stage == previousStage) + { + return; + } + HandleAreaChange(stage); } public void OnImGuiRender() @@ -2664,25 +2808,11 @@ namespace WorldTuningTool float width = ImGui.GetWindowWidth(); width /= width / 600.0f; - /* - if (ImGui.TextLink($"Current Stage: {Config.StageToString(currentStage)}")) - { - if (currentStage == 0) - { - selectedStage = globalStage; - } - else - { - selectedStage = currentStage; - } - } - */ ImGui.Text($"Current Stage: {Config.StageToString(currentStage)}"); - ImGui.PushItemWidth(width * 0.35f); if (ImGui.BeginCombo("##Stage", Config.StageToString(selectedStage), ImGuiComboFlags.HeightLarge)) { - foreach (Stage area in Enum.GetValues(typeof(Stage))) + foreach (StageExt area in Config.OrderedStages) { string name = Config.StageToString(area); bool isSelected = selectedStage == area; @@ -2695,23 +2825,27 @@ namespace WorldTuningTool ImGui.EndCombo(); } ImGui.SameLine(); + Config config = getConfig(); + bool invalidStage = !config.Overrides.ContainsKey(currentStage); + if (invalidStage) + { + ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); + } if (ImGui.Button("Current")) { - if (currentStage != 0) - { - selectedStage = currentStage; - } - else - { - selectedStage = globalStage; - } + selectedStage = currentStage; + } + if (invalidStage) + { + ImGui.PopStyleVar(); + ImGui.PopItemFlag(); } ImGui.SameLine(); if (ImGui.Button("Global")) { selectedStage = globalStage; } - Config config = getConfig(); List selectedOverrides = config.Overrides[selectedStage]; List? stageOverrides = maybeGetStageOverrides(config); List? globalOverrides = null; @@ -2722,11 +2856,12 @@ namespace WorldTuningTool if (ImGui.Button("Add override")) { Override ov = new Override(); - if (selectedStage == currentStage || selectedStage == globalStage) + if (selectedStage == currentStage) { ov.Set(); // Empty override never superseded. } selectedOverrides.Add(ov); + selectedOrderChanged = true; } ImGui.PopItemWidth(); for (int i = 0; i < selectedOverrides.Count; i++) @@ -2743,23 +2878,28 @@ namespace WorldTuningTool if (requestDown && i < selectedOverrides.Count - 1) { (selectedOverrides[i], selectedOverrides[i + 1]) = (selectedOverrides[i + 1], selectedOverrides[i]); + selectedOrderChanged = true; } ovS.Draw(selectedOverrides, null, globalOverrides, superseded, false, width); if (requestUp && i > 0) { (selectedOverrides[i], selectedOverrides[i - 1]) = (selectedOverrides[i - 1], selectedOverrides[i]); + selectedOrderChanged = true; } if (requestRemove) { if (!superseded) { + BeginTransition(); ovS.Unset(); if (ovS.Param != null && globalOverrides != null) { setIfOverridesContainsParam(globalOverrides, ovS.Param); } + EndTransition(); } selectedOverrides.RemoveAt(i); + selectedOrderChanged = true; i--; } ImGui.PopID(); @@ -2779,10 +2919,17 @@ namespace WorldTuningTool } saveConfig(); } + if (selectedOrderChanged) + { + ImGui.SameLine(); + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); + ImGui.Text("(Not Saved)"); + ImGui.PopStyleVar(); + } ImGui.Separator(); - // Global overrides and presets could now be show at the same time. + // We now have to consider both global and stage overrides. globalOverrides = config.Overrides[globalStage]; ImGui.PushID("Preset"); @@ -2799,6 +2946,8 @@ namespace WorldTuningTool } config.Presets[presetName] = new List(); selectedPreset = presetName; + config.SelectedPreset = selectedPreset; + saveConfig(); } ImGui.SameLine(); if (renamingPreset) @@ -2825,44 +2974,9 @@ namespace WorldTuningTool } if (prevPreset != selectedPreset) { - if (prevPreset != "") - { - List prevOverrides = config.Presets[prevPreset]; - foreach (Override ovR in prevOverrides) - { - bool superseded = ovR.Param != null && overridesContainsParam(externalOverrides.Values, ovR.Param); - if (!superseded) - { - ovR.Unset(); - if (ovR.Param != null) - { - if (!(stageOverrides != null && setIfOverridesContainsParam(stageOverrides, ovR.Param))) - { - setIfOverridesContainsParam(globalOverrides, ovR.Param); - } - } - } - } - } - if (selectedPreset != "") - { - List newOverrides = config.Presets[selectedPreset]; - foreach (Override ovN in newOverrides) - { - bool superseded = ovN.Param != null && overridesContainsParam(externalOverrides.Values, ovN.Param); - if (!superseded) - { - if (ovN.Param != null) - { - if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, ovN.Param))) - { - unsetIfOverridesContainsParam(globalOverrides, ovN.Param); - } - } - ovN.Set(); - } - } - } + HandlePresetChange(prevPreset, globalOverrides, stageOverrides); + config.SelectedPreset = selectedPreset; + saveConfig(); } if (renamingPreset) { @@ -2898,6 +3012,8 @@ namespace WorldTuningTool config.Presets.Remove(selectedPreset); config.Presets[presetName] = tmpOverrides; selectedPreset = presetName; + config.SelectedPreset = selectedPreset; + saveConfig(); } renamingPreset = false; } @@ -2919,6 +3035,7 @@ namespace WorldTuningTool Override ov = new Override(); ov.Set(); // Empty override never superseded. presetOverrides.Add(ov); + presetOrderChanged = true; } for (int i = 0; i < presetOverrides.Count; i++) { @@ -2934,16 +3051,19 @@ namespace WorldTuningTool if (requestDown && i < presetOverrides.Count - 1) { (presetOverrides[i], presetOverrides[i + 1]) = (presetOverrides[i + 1], presetOverrides[i]); + presetOrderChanged = true; } ovP.Draw(presetOverrides, stageOverrides, globalOverrides, superseded, true, width); if (requestUp && i > 0) { (presetOverrides[i], presetOverrides[i - 1]) = (presetOverrides[i - 1], presetOverrides[i]); + presetOrderChanged = true; } if (requestRemove) { if (!superseded) { + BeginTransition(); ovP.Unset(); if (ovP.Param != null) { @@ -2952,8 +3072,10 @@ namespace WorldTuningTool setIfOverridesContainsParam(globalOverrides, ovP.Param); } } + EndTransition(); } presetOverrides.RemoveAt(i); + presetOrderChanged = true; i--; } ImGui.PopID(); @@ -2973,6 +3095,13 @@ namespace WorldTuningTool } saveConfig(); } + if (presetOrderChanged) + { + ImGui.SameLine(); + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); + ImGui.Text("(Not Saved)"); + ImGui.PopStyleVar(); + } } ImGui.PopID(); -- cgit v1.2.3-101-g0448