//#define ENABLE_ASSERTS //#define SHADER_FEATURES using System.Numerics; using System.Runtime.InteropServices; #if ENABLE_ASSERTS using System.Diagnostics; #endif using ImGuiNET; using SharpPluginLoader.Core; using SharpPluginLoader.Core.Rendering; //using SharpPluginLoader.Core.Resources; using SharpPluginLoader.Core.Configuration; using SharpPluginLoader.Core.Memory; using SharpPluginLoader.Core.Entities; // @TODO: // - Descriptions. // - float format. // - Min/Max. // - Lights refactor. // - Lights iterop. // - SPL Patches: // - No crash on empty config. // - Style change // - Optimized update logic. // - Only update non-override values when ui is shown. // - Tune parameter settings (step, min/max, format, etc). // - PerFrame. // - Create light hook. // - Try to understand shadow distance vs backforward distance vs bias. // - Shadow distance retuning. // - Weird shadow area in Rotten Vale. // - Hotload nukes config? // // Known issues to think about: // - Volume rendering can look really bad with multiple sources(?) (Guding lands vines). // - Screen space reflections often look bad. // - Facial contact shadows incorrectly move based on camera position. namespace WorldTuningTool { public unsafe class Plugin : IPlugin { public string Name => "World Tuning Tool"; public string Author => "Akon City Software"; private static void Assert(bool condition) { #if ENABLE_ASSERTS Trace.Assert(condition); #endif } private static byte ByteFlag(bool f) { return f ? (byte)0x1 : (byte)0x0; } private static Vector4 ColorVectorFromInt(int c) { return new Vector4( ((c ) & 0xFF) / 255.0f, ((c >> 8 ) & 0xFF) / 255.0f, ((c >> 16) & 0xFF) / 255.0f, ((c >> 24) & 0xFF) / 255.0f); } private static int ColorVectorToInt(Vector4 v) { return ((int)MathF.Round(v.X * 255.0f) | ((int)MathF.Round(v.Y * 255.0f) << 8) | ((int)MathF.Round(v.Z * 255.0f) << 16) | ((int)MathF.Round(v.W * 255.0f) << 24)); } public enum ParameterType { BOOL, BYTE, FLAG, INT, HEX, COLOR, FLOAT, VECTOR3, VECTOR4, PATCH_FLOAT } static IPlugin? Instance = null; static Config getConfig() { return ConfigManager.GetConfig(Instance!); } private const float defaultParamWidth = 0.215f; private const Stage globalStage = Stage.InfinityOfNothingHandler; private static Stage currentStage => Area.CurrentStage; private static Stage selectedStage = globalStage; private static Stage previousStage = selectedStage; private static List? maybeGetStageOverrides(Config config) { return currentStage != 0 ? config.Overrides[currentStage] : null; } private static string selectedPreset = ""; private static bool renamingPreset = false; private static string typedPresetName = ""; private static Dictionary externalOverrides = new Dictionary(); private static int externalId = 0; private static bool overridesContainsParam(IEnumerable overrides, Parameter param) { foreach (Override ov in overrides) { if (ov.Param == param) { return true; } } return false; } private static bool unsetIfOverridesContainsParam(IEnumerable overrides, Parameter param) { foreach (Override ov in overrides) { if (ov.Param == param) { ov.Unset(); return true; } } return false; } private static bool setIfOverridesContainsParam(IEnumerable overrides, Parameter param) { foreach (Override ov in overrides) { if (ov.Param == param) { ov.Set(); return true; } } return false; } private static bool selectedOverrideSuperseded(Config config, List? globalOverrides, List? stageOverrides, Parameter? param) { bool superseded = false; if (param != null) { superseded |= globalOverrides == null && stageOverrides != null && overridesContainsParam(stageOverrides, param); superseded |= selectedPreset != "" && overridesContainsParam(config.Presets[selectedPreset], param); superseded |= overridesContainsParam(externalOverrides.Values, param); } return superseded; } private static bool stageOverrideSuperseded(Config config, Parameter? param) { bool superseded = false; if (param != null) { superseded |= selectedPreset != "" && overridesContainsParam(config.Presets[selectedPreset], param); superseded |= overridesContainsParam(externalOverrides.Values, param); } return superseded; } public abstract class Parameter { public string Name; private string hiddenName; public ParameterType Type; public bool PerFrame; private nint offset; private nint lastAddr = 0x0; private bool overrideValue = false; private bool overrideForStage = false; private bool overrideWasOn = false; protected float stepf, minf, maxf; protected int step, min, max; protected int mask; private Vector4 valueV = default; private int valueInt = 0; private Vector4 oValueV; private int oValueInt; private bool pendingUpdate = true; private bool queuedOverride = false; private bool queuedValue = false; private Vector4 qValueV; private int qValueInt; private bool bValue = false; private Vector4 bValueV; private int bValueInt; public Parameter(string name, nint offset, ParameterType type, bool perFrame) { Name = name; hiddenName = "##" + Name; this.offset = offset; Type = type; if (Type == ParameterType.PATCH_FLOAT) { valueV.X = MemoryUtil.Read(offset); pendingUpdate = false; } PerFrame = perFrame; } public bool PendingUpdate() { return pendingUpdate; } public (Vector4, int) GetValue() { return (valueV, valueInt); } public int GetMask() { return mask; } private void writeCurrentValue() { if (lastAddr == 0x0) return; switch (Type) { case ParameterType.BOOL: case ParameterType.BYTE: MemoryUtil.GetRef(lastAddr + offset) = (byte)valueInt; break; case ParameterType.FLAG: if (valueInt == 0) { MemoryUtil.GetRef(lastAddr + offset) &= ~mask; } else { MemoryUtil.GetRef(lastAddr + offset) |= mask; } break; case ParameterType.INT: case ParameterType.HEX: case ParameterType.COLOR: MemoryUtil.GetRef(lastAddr + offset) = valueInt; break; case ParameterType.FLOAT: MemoryUtil.GetRef(lastAddr + offset) = valueV.X; break; case ParameterType.VECTOR3: MemoryUtil.GetRef(lastAddr + offset) = new Vector3(valueV.X, valueV.Y, valueV.Z); break; case ParameterType.VECTOR4: MemoryUtil.GetRef(lastAddr + offset) = valueV; break; case ParameterType.PATCH_FLOAT: new Patch(offset, BitConverter.GetBytes(valueV.X)).Enable(); break; } } public void OverrideOn(bool forStage) { if (pendingUpdate) { if (queuedOverride) { Assert(!overrideWasOn && forStage); overrideWasOn = true; } else { queuedOverride = true; } } else { if (overrideValue) { Assert(!overrideWasOn && forStage); overrideWasOn = true; } else { oValueV = valueV; oValueInt = valueInt; if (!PerFrame) { writeCurrentValue(); } overrideValue = true; } } overrideForStage = forStage; } public void OverrideOff() { if (queuedOverride) { if (overrideWasOn) { Assert(overrideForStage); overrideWasOn = false; } else { queuedOverride = false; } } else { Assert(overrideValue); if (overrideWasOn) { Assert(overrideForStage); overrideWasOn = false; } else { valueV = oValueV; valueInt = oValueInt; bValue = false; if (!PerFrame) { writeCurrentValue(); } overrideValue = false; } } overrideForStage = false; } public void OverrideValue(Vector4 v4, int i1) { if (queuedOverride) { qValueV = v4; qValueInt = i1; queuedValue = true; } else { valueV = v4; valueInt = i1; if (!PerFrame) { writeCurrentValue(); } } } public void Update(nint baseObject) { lastAddr = baseObject; if (baseObject == 0x0) { pendingUpdate = true; return; } // Trying to track changes to the original value is a // "best effort" approach because it can't work if the in-game // value and the override value are the same. switch (Type) { case ParameterType.BOOL: case ParameterType.BYTE: { ref byte b1 = ref MemoryUtil.GetRef(baseObject + offset); if (b1 != (byte)valueInt && b1 != (byte)oValueInt) { oValueInt = (byte)b1; } if (overrideValue) { b1 = (byte)valueInt; } else { valueInt = b1; } break; } case ParameterType.FLAG: { ref int i1 = ref MemoryUtil.GetRef(baseObject + offset); int f1 = i1 & mask; if (f1 != valueInt && f1 != oValueInt) { oValueInt = f1; } if (overrideValue) { if (f1 == 0) { i1 &= ~mask; } else { i1 |= mask; } } else { valueInt = f1; } break; } case ParameterType.INT: case ParameterType.HEX: case ParameterType.COLOR: { ref int i1 = ref MemoryUtil.GetRef(baseObject + offset); if (i1 != valueInt && i1 != oValueInt) { oValueInt = i1; } if (overrideValue) { i1 = valueInt; } else { valueInt = i1; } break; } case ParameterType.FLOAT: { ref float f1 = ref MemoryUtil.GetRef(baseObject + offset); if (f1 != valueV.X && f1 != oValueV.X) { oValueV.X = f1; } if (overrideValue) { f1 = valueV.X; } else { valueV.X = f1; } break; } case ParameterType.VECTOR3: { ref Vector3 v3 = ref MemoryUtil.GetRef(baseObject + offset); Vector3 valueV3 = new Vector3(valueV.X, valueV.Y, valueV.Z); if (v3 != valueV3 && v3 != valueV3) { oValueV.X = v3.X; oValueV.Y = v3.Y; oValueV.Z = v3.Z; } if (overrideValue) { v3 = valueV3; } else { valueV.X = v3.X; valueV.Y = v3.Y; valueV.Z = v3.Z; } break; } case ParameterType.VECTOR4: { ref Vector4 v4 = ref MemoryUtil.GetRef(baseObject + offset); if (v4 != valueV && v4 != oValueV) { oValueV = v4; } if (overrideValue) { v4 = valueV; } else { valueV = v4; } break; } } if (pendingUpdate) { pendingUpdate = false; if (queuedOverride) { Assert(!overrideValue); OverrideOn(overrideForStage); if (queuedValue) { valueV = qValueV; valueInt = qValueInt; if (!PerFrame) { writeCurrentValue(); } queuedValue = false; } queuedOverride = false; } } } public bool DrawValue(ref Vector4 v4, ref int i1, float width, bool drawLabel) { bool valueChanged = false; string label = drawLabel ? Name : hiddenName; switch (Type) { case ParameterType.BOOL: { bool b = i1 == 0x1; if (ImGui.Checkbox(label, ref b)) { i1 = ByteFlag(b); valueChanged = true; } break; } case ParameterType.BYTE: { ImGui.SetNextItemWidth(width * defaultParamWidth); ImGuiInputTextFlags flags = ImGuiInputTextFlags.EnterReturnsTrue; if (ImGui.InputInt(label, ref i1, step, 0, flags)) { i1 = Math.Clamp(i1, min, max); valueChanged = true; } break; } case ParameterType.INT: case ParameterType.HEX: { ImGui.SetNextItemWidth(width * defaultParamWidth); ImGuiInputTextFlags flags = ImGuiInputTextFlags.EnterReturnsTrue; if (Type == ParameterType.HEX) { flags |= ImGuiInputTextFlags.CharsHexadecimal; } if (ImGui.InputInt(label, ref i1, step, 0, flags)) { if (max > min) { i1 = Math.Clamp(i1, min, max); } valueChanged = true; } break; } case ParameterType.FLAG: { bool b = i1 == mask; if (ImGui.Checkbox(label, ref b)) { i1 = b ? mask : 0; valueChanged = true; } break; } case ParameterType.COLOR: { ImGui.SetNextItemWidth(width * defaultParamWidth * 3.0f); Vector4 colorV = ColorVectorFromInt(i1); if (ImGui.ColorEdit4(label, ref colorV)) { i1 = ColorVectorToInt(colorV); valueChanged = true; } break; } case ParameterType.FLOAT: { ImGui.SetNextItemWidth(width * defaultParamWidth); float f1 = v4.X; // Ideally we want ImGuiInputTextFlags.EnterReturnsTrue behavior here. if (ImGui.DragFloat(label, ref f1, stepf, minf, maxf, "%.5f", ImGuiSliderFlags.NoRoundToFormat)) { v4.X = f1; valueChanged = true; } break; } case ParameterType.VECTOR3: { ImGui.SetNextItemWidth(width * defaultParamWidth * 3.0f); Vector3 v3 = new Vector3(v4.X, v4.Y, v4.Z); if (ImGui.DragFloat3(label, ref v3, stepf, minf, maxf, "%.4f", ImGuiSliderFlags.NoRoundToFormat)) { v4.X = v3.X; v4.Y = v3.Y; v4.Z = v3.Z; valueChanged = true; } break; } case ParameterType.VECTOR4: { ImGui.SetNextItemWidth(width * defaultParamWidth * 4.0f); if (ImGui.DragFloat4(label, ref v4, stepf, minf, maxf, "%.4f", ImGuiSliderFlags.NoRoundToFormat)) { valueChanged = true; } break; } case ParameterType.PATCH_FLOAT: { ImGui.SetNextItemWidth(width * defaultParamWidth); float f1 = v4.X; if (ImGui.InputFloat(label, ref f1, 0.0f, 0.0f, "%.4f", ImGuiInputTextFlags.EnterReturnsTrue)) { v4.X = f1; valueChanged = true; } break; } } return valueChanged; } public void DrawReferenceValue(string name, Vector4 v4, int i1) { ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.7f); switch (Type) { case ParameterType.BOOL: { ImGui.Text($"({name}: {i1 == 0x1})"); break; } case ParameterType.BYTE: { ImGui.Text($"({name}: {i1})"); break; } case ParameterType.FLAG: { ImGui.Text($"({name}: {i1 != 0})"); break; } case ParameterType.INT: case ParameterType.HEX: case ParameterType.COLOR: { if (Type == ParameterType.INT) { ImGui.Text($"({name}: {i1})"); } else { ImGui.Text($"({name}: {i1:X})"); } break; } case ParameterType.FLOAT: case ParameterType.PATCH_FLOAT: { ImGui.Text($"({name}: {v4.X:0.00000})"); break; } case ParameterType.VECTOR3: { ImGui.Text($"({name}: {v4.X:0.0000}, {v4.Y:0.0000}, {v4.Z:0.0000})"); break; } case ParameterType.VECTOR4: { ImGui.Text($"({name}: {v4.X:0.0000}, {v4.Y:0.0000}, {v4.Z:0.0000}, {v4.W:0.00000})"); break; } } ImGui.PopStyleVar(); } public void Draw(float width, bool assumeOverride = false) { Draw(ref valueV, ref valueInt, width, assumeOverride); } public void Draw(ref Vector4 v4, ref int i1, float width, bool assumeOverride) { ImGui.PushID(Name); if (!assumeOverride) { if (overrideForStage) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushItemFlag(ImGuiItemFlags.MixedValue, true); } bool toggleOverride = overrideValue; if (ImGui.Checkbox("##Override", ref toggleOverride)) { if (toggleOverride) { OverrideOn(false); } else { OverrideOff(); } } if (ImGui.BeginItemTooltip()) { if (overrideForStage) { ImGui.Text("Set by an Override"); } else { ImGui.Text("Override"); } ImGui.EndTooltip(); } if (overrideForStage) { ImGui.PopItemFlag(); ImGui.PopItemFlag(); } ImGui.SameLine(); } if (!overrideValue) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); } if (DrawValue(ref valueV, ref valueInt, width, !bValue) && !PerFrame) { writeCurrentValue(); } if (!overrideValue) { ImGui.PopItemFlag(); } Config config = getConfig(); bool showButtons = !assumeOverride && overrideValue && !pendingUpdate; if (showButtons) { ImGui.SameLine(); if (!bValue) { if (ImGui.Button("B")) { bValueV = oValueV; bValueInt = oValueInt; bValue = true; } } else { if (ImGui.Button("A/B")) { (bValueV, valueV) = (valueV, bValueV); (bValueInt, valueInt) = (valueInt, bValueInt); if (!PerFrame) { writeCurrentValue(); } } ImGui.SameLine(); DrawValue(ref bValueV, ref bValueInt, width, true); ImGui.SameLine(); if (ImGui.Button("< B")) { bValue = false; } } List selectedOverrides = config.Overrides[selectedStage]; List? stageOverrides = maybeGetStageOverrides(config); List? globalOverrides = null; if (selectedStage != globalStage) { globalOverrides = config.Overrides[globalStage]; } if (selectedPreset != "") { List presetOverrides = config.Presets[selectedPreset]; if (!overridesContainsParam(presetOverrides, this)) { ImGui.SameLine(); if (ImGui.Button("▲")) { // Create override before unsetting global. Override ov = new Override(this, v4, i1); bool superseded = overridesContainsParam(externalOverrides.Values, this); if (!superseded) { if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, this))) { unsetIfOverridesContainsParam(config.Overrides[globalStage], this); } ov.Set(); } presetOverrides.Add(ov); } if (ImGui.BeginItemTooltip()) { ImGui.Text($"Add to '{selectedPreset}'"); ImGui.EndTooltip(); } } } else if (!overridesContainsParam(selectedOverrides, this)) { ImGui.SameLine(); if (ImGui.Button("▲")) { // Create override before unsetting global. Override ov = new Override(this, v4, i1); bool superseded = selectedOverrideSuperseded(config, globalOverrides, stageOverrides, this); if (!superseded) { if (globalOverrides != null) { unsetIfOverridesContainsParam(globalOverrides, this); } ov.Set(); } selectedOverrides.Add(ov); } if (ImGui.BeginItemTooltip()) { ImGui.Text($"Add to '{Config.StageToString(selectedStage)}'"); ImGui.EndTooltip(); } } ImGui.SameLine(); DrawReferenceValue("Original", oValueV, oValueInt); } ImGui.PopID(); } } public class Parameter : Parameter where T : unmanaged { // @TODO: Defaults are ill-defined. Should have no defaults except for type sizes (byte). public Parameter(string name, nint offset, ParameterType type, bool perFrame, T? step = null, T? min = null, T? max = null) : base(name, offset, type, perFrame) { if (typeof(T) == typeof(float)) { this.stepf = step != null ? Convert.ToSingle(step) : 0.01f; this.minf = min != null ? Convert.ToSingle(min) : -Single.MaxValue; this.maxf = max != null ? Convert.ToSingle(max) : Single.MaxValue; } else if (typeof(T) == typeof(int) || typeof(T) == typeof(byte)) { this.step = step != null ? Convert.ToInt32(step) : 0; mask = this.step; if (typeof(T) == typeof(int)) { this.min = min != null ? Convert.ToInt32(min) : Int32.MinValue; this.max = max != null ? Convert.ToInt32(max) : Int32.MaxValue; } else if (typeof(T) == typeof(byte)) { this.min = min != null ? Convert.ToInt32(min) : 0x0; this.max = max != null ? Convert.ToInt32(max) : 0xFF; } } } } private static Parameter? getParameterByName(string name) { foreach (Parameter param in allParameters) { if (param.Name == name) { return param; } } return null; } public class Override { public Parameter? Param = null; public string Name { get { return Param != null ? Param.Name : ""; } set { Param = getParameterByName(value); } } public string Value { get { if (Param != null) { switch (Param.Type) { case ParameterType.BOOL: return (valueInt == 0x1).ToString(); case ParameterType.BYTE: return valueInt.ToString(); case ParameterType.FLAG: return (valueInt != 0).ToString(); case ParameterType.INT: return valueInt.ToString(); case ParameterType.HEX: case ParameterType.COLOR: return valueInt.ToString("X8"); case ParameterType.FLOAT: case ParameterType.PATCH_FLOAT: return valueV.X.ToString(); case ParameterType.VECTOR3: { Vector3 valueV3 = new Vector3(valueV.X, valueV.Y, valueV.Z); return valueV3.ToString(); } case ParameterType.VECTOR4: return valueV.ToString(); } } return ""; } set { if (Param == null) return; try { switch (Param.Type) { case ParameterType.BOOL: valueInt = ByteFlag(Convert.ToBoolean(value)); break; case ParameterType.BYTE: valueInt = Convert.ToInt32(value); break; case ParameterType.FLAG: valueInt = Convert.ToBoolean(value) ? Param.GetMask() : 0; break; case ParameterType.INT: valueInt = Convert.ToInt32(value); break; case ParameterType.HEX: case ParameterType.COLOR: valueInt = Convert.ToInt32(value, 16); break; case ParameterType.FLOAT: case ParameterType.PATCH_FLOAT: valueV.X = Convert.ToSingle(value); break; case ParameterType.VECTOR3: break; case ParameterType.VECTOR4: break; } sValueV = valueV; sValueInt = valueInt; } catch (FormatException) { } catch (OverflowException) { } } } private Vector4 valueV = default; private int valueInt = 0; private bool isSet = false; private Vector4 sValueV = default; private int sValueInt = 0; public Override() { } public Override(Parameter param, Vector4 v4, int i1) { Param = param; valueV = v4; valueInt = i1; Save(); } public void Save() { (sValueV, sValueInt) = (valueV, valueInt); } public void Set() { Assert(!isSet); isSet = true; if (Param != null) { Param.OverrideOn(true); Param.OverrideValue(valueV, valueInt); } } public void Unset() { Assert(isSet); isSet = false; if (Param != null) { Param.OverrideOff(); } } public void Draw(List? currentOverrides, List? stageOverrides, List? globalOverrides, bool superseded, float width) { if (superseded) // superseded == !isSet. { Assert(!isSet); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (currentOverrides == null || superseded) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); } ImGui.PushItemWidth(width * 0.475f); if (ImGui.BeginCombo("##Parameters", Param != null ? Param.Name : "")) { if (currentOverrides != null) { foreach (Parameter iterParam in allParameters) { if (overridesContainsParam(currentOverrides, iterParam)) continue; bool isSelected = Param == iterParam; if (ImGui.Selectable(iterParam.Name, isSelected)) { bool wasSet = isSet; if (wasSet) { Unset(); if (Param != null) { if (!(stageOverrides != null && setIfOverridesContainsParam(stageOverrides, Param))) { if (globalOverrides != null) { setIfOverridesContainsParam(globalOverrides, Param); } } } if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, iterParam))) { if (globalOverrides != null) { unsetIfOverridesContainsParam(globalOverrides, iterParam); } } } Param = iterParam; (valueV, valueInt) = Param.GetValue(); Save(); if (wasSet) // If this override is superseded, expect this Combo to be disabled. { Set(); } } if (isSelected) ImGui.SetItemDefaultFocus(); } } ImGui.EndCombo(); } ImGui.PopItemWidth(); if (currentOverrides == null || superseded) { ImGui.PopItemFlag(); } if (Param != null) { ImGui.SameLine(); if (Param.DrawValue(ref valueV, ref valueInt, width, false) && isSet) { Param.OverrideValue(valueV, valueInt); } (Vector4 v4, int i1) = Param.GetValue(); bool valueDiffers = (v4, i1) != (valueV, valueInt); if ((valueV, valueInt) != (sValueV, sValueInt) || (isSet && valueDiffers && !Param.PendingUpdate())) { ImGui.SameLine(); if (ImGui.Button("⇄")) { valueV = sValueV; valueInt = sValueInt; if (isSet) { Param.OverrideValue(valueV, valueInt); } } } if (superseded) { ImGui.PopStyleVar(); } if (Param.PendingUpdate()) { ImGui.SameLine(); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.7f); ImGui.Text("(Pending)"); ImGui.PopStyleVar(); if (ImGui.BeginItemTooltip()) { ImGui.Text("The type this parameter belongs to has no current object."); ImGui.EndTooltip(); } } else if (valueDiffers || superseded) { ImGui.SameLine(); Param.DrawReferenceValue("Current", v4, i1); } } else if (superseded) { ImGui.PopStyleVar(); } } } private delegate void OnAreaChange(nint unknownPtr); private Hook? onAreaChange; private static MtObject? sMhScene = null; private delegate void UpdateSSAOParams(nint sceneObjectInternal, nint stackOffset); private Hook? updateSSAOParams; private static Parameter[] ssaoParameters = { new Parameter("SSAO Depth Bias", 0xB4B0, ParameterType.FLOAT, false, 0.00001f), new Parameter("SSAO Sloped Depth Bias", 0xB4B4, ParameterType.FLOAT, false, 0.00001f), new Parameter("SSAO Max Depth Bias", 0xB4B8, ParameterType.FLOAT, false, 0.00001f), new Parameter("SSAO Dispersion", 0xB4BC, ParameterType.FLOAT, false), new Parameter("SSAO Effect", 0xB430, ParameterType.FLOAT, false), new Parameter("SSAO Effect GI", 0xB434, ParameterType.FLOAT, false), new Parameter("SSAO Depth Difference", 0xB4C0, ParameterType.FLOAT, false), new Parameter("SSAO Samples Per Pixel", 0xB4C4, ParameterType.FLOAT, false), new Parameter("SSAO Max Sample Num", 0xB4C8, ParameterType.INT, false), new Parameter("SSAO Max Sample Num (HQ)", 0xB4D0, ParameterType.INT, false), new Parameter("SSAO Radius", 0xB4D4, ParameterType.FLOAT, false), new Parameter("SSAO Bias", 0xB4D8, ParameterType.FLOAT, false), new Parameter("SSAO Intensity", 0xB4E0, ParameterType.FLOAT, false), new Parameter("SSAO Use HiZ", 0xB4E5, ParameterType.BOOL, false), new Parameter("SSAO Edge Atten Rate", 0xB4DC, ParameterType.FLOAT, false) }; private delegate void UpdateSSLRParams(nint stackOffset); private Hook? updateSSLRParams; private static Parameter[] sslrParameters = { new Parameter("SSLR Loop Count", 0xE628, ParameterType.INT, false), new Parameter("SSLR Loop Count Factor for CBR", 0xE62C, ParameterType.FLOAT, false), new Parameter("SSLR Eliminate Depth", 0xE630, ParameterType.FLOAT, false), new Parameter("SSLR Accurate Threshold", 0xE644, ParameterType.FLOAT, false), new Parameter("SSLR Accurate Threshold (HQ)", 0xE64C, ParameterType.FLOAT, false), new Parameter("SSLR Dither Radius", 0xE634, ParameterType.FLOAT, false), new Parameter("SSLR Importance Bias", 0xE638, ParameterType.FLOAT, false), new Parameter("SSLR Mip Scale", 0xE63C, ParameterType.FLOAT, false), new Parameter("SSLR Mip Bias", 0xE640, ParameterType.FLOAT, false), new Parameter("SSLR Dither Resolve", 0xB43F, ParameterType.BOOL, false), new Parameter("SSLR Edge Atten Rate", 0xB428, ParameterType.FLOAT, false), new Parameter("SSLR Mip 0 Count Threshold", 0xB438, ParameterType.INT, false, 1), new Parameter("SSLR Depth Eliminate Rate", 0xB42C, ParameterType.FLOAT, false), new Parameter("SSLR Use Mipmap", 0xE650, ParameterType.BOOL, false), new Parameter("SSLR GBuffer Jitter", 0xB440, ParameterType.BOOL, false), }; private static Parameter[] sceneParameters = { new Parameter("HQ Mode", 0xE9A3, ParameterType.BOOL, false), new Parameter("Shadow Cascade 2Way Bias (HQ)", 0x58, ParameterType.FLOAT, false), new Parameter("Primary Shadow Sample Num", 0xE5C4, ParameterType.INT, false), new Parameter("Primary Shadow Sample Num (HQ)", 0xE5CC, ParameterType.INT, false), new Parameter("Primary Shadow HQ", 0xE5C0, ParameterType.BOOL, false), new Parameter("Primary Shadow HQ (HQ)", 0xE5C2, ParameterType.BOOL, false), new Parameter("LOD Bias 1", 0x21C, ParameterType.FLOAT, false), new Parameter("LOD Bias 2", 0x220, ParameterType.FLOAT, false), new Parameter("LOD Caster Bias", 0x1E4, ParameterType.FLOAT, false), new Parameter("LOD Length Bias", 0x1F4, ParameterType.FLOAT, false), new Parameter("LOD Length Bias (HQ)", 0x1FC, ParameterType.FLOAT, false), new Parameter("LOD Pixel Size Bias", 0x200, ParameterType.FLOAT, false), new Parameter("LOD Pixel Size Bias (HQ)", 0x208, ParameterType.FLOAT, false), new Parameter("LOD Limit", 0xE898, ParameterType.INT, false, 1), new Parameter("LOD Passthrough Culling Rate", 0x1F0, ParameterType.FLOAT, false), new Parameter("Far Culling Fade Length Max", 0x20C, ParameterType.FLOAT, false), new Parameter("Far Culling Fade Pixel Max", 0x210, ParameterType.FLOAT, false), new Parameter("Speed Tree LOD Billboard Fade Range", 0x214, ParameterType.FLOAT, false), new Parameter("LOD Culling Length Bias", 0x224, ParameterType.FLOAT, false), new Parameter("LOD Culling Pixel Bias", 0x228, ParameterType.FLOAT, false), new Parameter("Is Calc LOD Bias Use Boundary", 0x218, ParameterType.BOOL, false), new Parameter("Model Detail", 0x190, ParameterType.INT, false, 1), new Parameter("Passthrough Active", 0xE9A0, ParameterType.BOOL, false), new Parameter("Passthrough Culling Active", 0xE9A2, ParameterType.BOOL, false), new Parameter("Passthrough Near", 0xE940, ParameterType.FLOAT, false), new Parameter("Passthrough Far", 0xE944, ParameterType.FLOAT, false), new Parameter("Passthrough Near Alpha", 0xE948, ParameterType.FLOAT, false), new Parameter("Passthrough Far Alpha", 0xE94C, ParameterType.FLOAT, false), new Parameter("Passthrough Correct", 0xE950, ParameterType.FLOAT, false), new Parameter("LOD Length Platform Bias[2]", 0x22C + (4 * 2), ParameterType.FLOAT, false), new Parameter("LOD Pixel Size Platform Bias[2]", 0x244 + (4 * 2), ParameterType.FLOAT, false), new Parameter("Fake Light Intensity", 0xEC40, ParameterType.FLOAT, false), // MonsterHunterWorld.exe+1B18711 - mov [rbx+0000EC44],3F800000 (Other unknown reference). new Parameter("Fake Light Intensity (Gameplay)", new IntPtr(0x141B1A19F) + 0x6, ParameterType.PATCH_FLOAT, false), new Parameter("Fake Light Blend", new IntPtr(0x141CDE0CE) + 0x6, ParameterType.PATCH_FLOAT, false), new Parameter("Contact Shadows Enabled", 0xB4EC, ParameterType.BOOL, false), new Parameter("Force Disable Contact Shadows", 0xB4ED, ParameterType.BOOL, false), new Parameter("Facial Contact Shadows Enabled", 0xB4EE, ParameterType.BOOL, false), new Parameter("Contact Shadows Enable Noise", 0xB500, ParameterType.BOOL, false), new Parameter("Facial Contact Shadows Enable Noise", 0xB501, ParameterType.BOOL, false), new Parameter("Contact Shadow Intensity", 0xB4F0, ParameterType.FLOAT, false), new Parameter("Contact Shadow Length", 0xB4F4, ParameterType.FLOAT, false), new Parameter("Contact Shadow Accept Maximum Length", 0xB4F8, ParameterType.FLOAT, false), new Parameter("Contact Shadow Accept Minimum Length", 0xB4FC, ParameterType.FLOAT, false), new Parameter("Capsule AO Enabled", 0xB502, ParameterType.BOOL, false), new Parameter("Force Disable Capsule AO", 0xB503, ParameterType.BOOL, false), new Parameter("Capsule AO Distance Fall Coef", 0xE5B4, ParameterType.FLOAT, false), new Parameter("Capsule AO Light Channel Mask", 0xE5BC, ParameterType.INT, false), new Parameter("Capsule Light Dir XZY", 0xE548, ParameterType.VECTOR3, false), new Parameter("Capsule Light W Dir", 0xE560, ParameterType.FLOAT, false), new Parameter("Capsule Light Angle", 0xE5B0, ParameterType.FLOAT, false), new Parameter("Capsule AO Intensity", 0xE5B8, ParameterType.FLOAT, false), new Parameter("Accurate Cascade 1 Shadow", 0x44F, ParameterType.BOOL, false), new Parameter("Broad Area Shadow Enable", 0x5534, ParameterType.BOOL, false), new Parameter("Broad Area Shadow Center", 0x5540, ParameterType.VECTOR3, false), new Parameter("Broad Area Shadow Range", 0x5560, ParameterType.VECTOR3, false), new Parameter("Broad Area Shadow Dir", 0x5550, ParameterType.VECTOR4, false), new Parameter("Broad Area Shadow Max LOD Level", 0x5524, ParameterType.FLOAT, false), new Parameter("Broad Area Shadow Culling Size", 0x5528, ParameterType.FLOAT, false), new Parameter("Broad Area Shadow Precision", 0x5518, ParameterType.INT, false, 1, 0, 2), // Maybe 3? new Parameter("Shadow Culling Length", 0x5C, ParameterType.FLOAT, false), new Parameter("Light Frustum Cull Enable", 0x552C, ParameterType.BOOL, false), new Parameter("Snow Field 4 Global LOD Param", 0x5718, ParameterType.FLOAT, false) }; private delegate nint CreateFXAAObject(); private delegate nint CreateFXAAObject2(nint unknownPtr); private delegate nint DestroyFXAAObject(nint fxaaObjectInternal, int unknownInt); private Hook? createFXAAObject; private Hook? createFXAAObject2; private Hook? destroyFXAAObject; private nint fxaaObject = 0x0; private static Parameter[] fxaaParameters = { new Parameter("FXAA Subpix", 0x178, ParameterType.FLOAT, false, 0.005f, 0.0f, 1.0f), new Parameter("FXAA Edge Threshold", 0x17C, ParameterType.FLOAT, false, 0.001f, 0.0f, 1.0f), new Parameter("FXAA Edge Threshold Min", 0x180, ParameterType.FLOAT, false, 0.001f, 0.0f, 1.0f) }; private delegate nint CreateDofObject(); private delegate nint CreateDofObject2(nint unknownPtr); private delegate nint DestroyDofObject(nint dofObjectInternal, int unknownInt); private Hook? createDofObject; private Hook? createDofObject2; private Hook? destroyDofObject; private delegate void UpdateDofParams(nint unknownPtr); private Hook? updateDofParams; private nint dofObject = 0x0; private static Parameter[] dofParameters = { new Parameter("Dof Enabled", 0x1DD, ParameterType.BOOL, false), new Parameter("New Version", 0x1DC, ParameterType.BOOL, false), new Parameter("F Number", 0x1A0, ParameterType.FLOAT, true, 0.001f), new Parameter("Sensor Size", 0x1A4, ParameterType.FLOAT, true, 0.001f), new Parameter("Focus Distance", 0x1A8, ParameterType.FLOAT, true, 0.2f), new Parameter("Near Coef", 0x1CC, ParameterType.FLOAT, false, 0.001f), new Parameter("Far Coef", 0x1C8, ParameterType.FLOAT, false, 0.001f), new Parameter("Near Enable", 0x1DE, ParameterType.BOOL, false), new Parameter("Far Enable", 0x1DF, ParameterType.BOOL, false), new Parameter("Debug Draw", 0x1E0, ParameterType.BOOL, false), new Parameter("Radius", 0x1D0, ParameterType.FLOAT, false, 0.001f), new Parameter("Depth Scale Foreground", 0x1D4, ParameterType.FLOAT, false, 0.0001f), new Parameter("Aspect", 0x1D8, ParameterType.FLOAT, false, 0.001f), new Parameter("Vignetting Enabled", 0x200, ParameterType.BOOL, true), new Parameter("Vignetting Ellipse", 0x201, ParameterType.BOOL, true), new Parameter("Vignetting Ellipticity", 0x204, ParameterType.FLOAT, true), new Parameter("Vignetting Offset", 0x1F8, ParameterType.FLOAT, true), new Parameter("Vignetting Pow", 0x1FC, ParameterType.FLOAT, true), new Parameter("Vignetting Color", 0x208, ParameterType.COLOR, true), }; private delegate nint CreateMotionBlurObject(); private delegate nint CreateMotionBlurObject2(nint unknownPtr); private delegate nint DestroyMotionBlurObject(nint motionBlurObjectInternal, int unknownInt); private Hook? createMotionBlurObject; private Hook? createMotionBlurObject2; private Hook? destroyMotionBlurObject; private delegate void UpdateMotionBlurParams(nint unknownPtr, nint unknownPtr2); private Hook? updateMotionBlurParams; private nint motionBlurObject = 0x0; private static Parameter[] motionBlurParameters = { new Parameter("Type", 0x168, ParameterType.INT, false, 1), new Parameter("Sample Num", 0x180, ParameterType.INT, false, 1, 0), new Parameter("Shutter Speed", 0x184, ParameterType.FLOAT, true), new Parameter("Fur Shutter Speed", 0x188, ParameterType.FLOAT, false), new Parameter("Blur Threshold", 0x18C, ParameterType.FLOAT, false) }; private delegate nint CreateBloomObject(); private delegate nint CreateBloomObject2(nint unknownPtr); private delegate nint DestroyBloomObject(nint bloomObjectInternal, int unknownInt); private Hook? createBloomObject; private Hook? createBloomObject2; private Hook? destroyBloomObject; private nint bloomObject = 0x0; private static Parameter[] bloomParameters = { new Parameter("Bloom Enabled", 0x14, ParameterType.FLAG, false, (1 << 1)), new Parameter("Threshold", 0x204, ParameterType.FLOAT, false), new Parameter("Renormalize", 0x208, ParameterType.FLOAT, false), new Parameter("Dispersion for CBR", 0x210, ParameterType.FLOAT, false), new Parameter("is SRGB Gamut", 0x214, ParameterType.BOOL, false), new Parameter("Enable Optimize", 0x215, ParameterType.BOOL, false), new Parameter("Bloom Color", 0x200, ParameterType.COLOR, false), new Parameter("Dirt Color", 0x230, ParameterType.FLOAT, false), new Parameter("Flip Dirt Intensity", 0x240, ParameterType.BOOL, false), new Parameter("Downsample Count", 0x168, ParameterType.INT, false), new Parameter("Reduction Resolution", 0x16C, ParameterType.INT, false), new Parameter("Compute Luminance", 0x241, ParameterType.BOOL, false) }; private static MtObject? sLightProbes = null; private static Parameter[] lightProbesParameters = { new Parameter("Debug Mode", 0x98, ParameterType.INT, false, 1), }; private delegate nint CreateLightingObject(); private delegate nint DestroyLightingObject(nint lightingObjectInternal, int unknownInt); private Hook? createLightingObject; private Hook? destroyLightingObject; private nint lightingObject = 0x0; private static Parameter[] lightingParameters = { new Parameter("Tone Map Type", 0x16C, ParameterType.INT, false, 1, 1, 7), new Parameter("Shoulder Strength", 0x168, ParameterType.FLOAT, false), new Parameter("Linear Strength", 0x178, ParameterType.FLOAT, false), new Parameter("Linear Angle", 0x17C, ParameterType.FLOAT, false), new Parameter("Toe Strength", 0x180, ParameterType.FLOAT, false), new Parameter("Toe Num", 0x184, ParameterType.FLOAT, false), new Parameter("White Point", 0x18C, ParameterType.FLOAT, false), new Parameter("Enable Color Grading", 0x1D8, ParameterType.BOOL, false), new Parameter("Dispersion", 0x19C, ParameterType.FLOAT, false), new Parameter("Edge Sharpness", 0x198, ParameterType.FLOAT, false, 0.00001f), new Parameter("Downsample Volume", 0x210, ParameterType.BOOL, false) }; private delegate void UpdateShadowParams(nint shadowObjectInternal, nint stackOffset); private Hook? updateShadowParams; private Hook? staticShadowParams; private static Parameter[] shadowParameters1 = { }; // This is actually an InfiniteLight object. private static Parameter[] shadowParameters2 = { new Parameter("Color", 0xE0, ParameterType.FLOAT, true), new Parameter("Intensity", 0xF4, ParameterType.FLOAT, true), new Parameter("Min Roughness", 0xFC, ParameterType.FLOAT, true), new Parameter("Dir", 0x110, ParameterType.VECTOR4, true), new Parameter("Group", 0x14, ParameterType.BYTE, true), new Parameter("Priority", 0x1A, ParameterType.BYTE, true), new Parameter("Primary", 0x1D, ParameterType.BOOL, true), new Parameter("Shadow Cast", 0x23, ParameterType.BOOL, true), new Parameter("Near Clip Distance", 0x28, ParameterType.FLOAT, true), new Parameter("Shadow Sloped Depth Bias", 0x40, ParameterType.FLOAT, true, 0.00001f), new Parameter("Shadow Map Size", 0x30, ParameterType.INT, true), // Aligned to 128. new Parameter("Shadow Depth Bias", 0x38, ParameterType.FLOAT, true, 0.00001f), new Parameter("Shadow Max Depth Bias", 0x48, ParameterType.FLOAT, true, 0.00001f), new Parameter("Do Volumetric", 0x1F, ParameterType.BOOL, true), new Parameter("Draw Mode", 0x21, ParameterType.BYTE, true), new Parameter("Shadow Distance", 0x6C, ParameterType.FLOAT, true, 5.0f), new Parameter("Shadow Backforward Distance", 0x7C, ParameterType.FLOAT, true, 5.0f), new Parameter("Shadow Distribution", 0x74, ParameterType.FLOAT, true, 0.001f), new Parameter("Manual Split", 0x4D, ParameterType.BOOL, true), new Parameter("Individual Shadow Bias", 0x81, ParameterType.BOOL, true), new Parameter("Manual Split Distance[0]", 0x54, ParameterType.FLOAT, true), new Parameter("Cascade Depth Bias[0]", 0x88, ParameterType.FLOAT, true, 0.00001f), new Parameter("Cascade Sloped Depth Bias[0]", 0xA0, ParameterType.FLOAT, true, 0.00001f), new Parameter("Cascade Max Depth Bias[0]", 0xB8, ParameterType.FLOAT, true, 0.00001f), new Parameter("Manual Split Distance[1]", 0x5C, ParameterType.FLOAT, true), new Parameter("Cascade Depth Bias[1]", 0x90, ParameterType.FLOAT, true, 0.00001f), new Parameter("Cascade Sloped Depth Bias[1]", 0xA8, ParameterType.FLOAT, true, 0.00001f), new Parameter("Cascade Max Depth Bias[1]", 0xC0, ParameterType.FLOAT, true, 0.00001f), new Parameter("Manual Split Distance[2]", 0x64, ParameterType.FLOAT, true), new Parameter("Cascade Depth Bias[2]", 0x98, ParameterType.FLOAT, true, 0.00001f), new Parameter("Cascade Sloped Depth Bias[2]", 0xB0, ParameterType.FLOAT, true, 0.00001f), new Parameter("Cascade Max Depth Bias[2]", 0xC8, ParameterType.FLOAT, true, 0.00001f), new Parameter("Projection Scale", 0x134, ParameterType.FLOAT, true), new Parameter("Projection Offset Speed", 0x144, ParameterType.FLOAT, true), new Parameter("Projection Up", 0x160, ParameterType.VECTOR3, true), new Parameter("Shadow Cascade Mode", 0x174, ParameterType.BYTE, true, 1, 0, 3), new Parameter("Shadow Cascade 2Way Bias", 0x17C, ParameterType.FLOAT, true), new Parameter("Shadow Culling Length[0]", 0x184, ParameterType.FLOAT, true), new Parameter("Shadow Culling Length[1]", 0x18C, ParameterType.FLOAT, true), new Parameter("Shadow Culling Length[2]", 0x194, ParameterType.FLOAT, true), //new Parameter("Broad Area Shadow Range", 0x1B0, ParameterType.VECTOR3, true), new Parameter("Broad Area Shadow Depth Bias", 0x1C4, ParameterType.FLOAT, true, 0.00001f), new Parameter("Broad Area Shadow Sloped Depth Bias", 0x1CC, ParameterType.FLOAT, true, 0.00001f), new Parameter("Broad Area Shadow Max Depth Bias", 0x1D4, ParameterType.FLOAT, true, 0.00001f), new Parameter("Primary Shadow Sample Num", 0x1DC, ParameterType.INT, true), new Parameter("Primary Shadow Radius", 0x1EC, ParameterType.FLOAT, true, 0.0001f), new Parameter("Fov", 0x1F4, ParameterType.FLOAT, true, 0.0001f), new Parameter("Is Fixed Fov Mode", 0x1F9, ParameterType.BOOL, true), new Parameter("Discretization Angle", 0x200, ParameterType.FLOAT, true, 0.0001f), new Parameter("Is Discretization Fov Mode", 0x205, ParameterType.BOOL, true) }; private static MtObject? sMain = null; private static MtObject? sLightLinker = null; private static MtObject? sMhSpeedTree = null; private static Parameter[] allParameters = sceneParameters .Concat(ssaoParameters) .Concat(sslrParameters) .Concat(fxaaParameters) .Concat(dofParameters) .Concat(motionBlurParameters) .Concat(bloomParameters) .Concat(lightingParameters) .Concat(shadowParameters2).ToArray(); private bool tripleShadowResolution = false; private bool quadrupleShadowResolution = false; private NativeAction setShadowQuality; private Patch shadowRes1_3x; private Patch shadowRes1_4x; private Patch shadowRes1_1; private Patch shadowRes1_2; private Patch shadowRes1_3; private Patch shadowRes2; private Patch shadowRes3; //private Patch shadowRes4_Limit; private void tripleShadowResEnable() { shadowRes1_3x.Enable(); shadowRes1_1.Enable(); shadowRes1_2.Enable(); shadowRes1_3.Enable(); shadowRes2.Enable(); shadowRes3.Enable(); //shadowRes4_Limit.Enable(); } private void tripleShadowResDisable() { shadowRes1_3x.Disable(); shadowRes1_1.Disable(); shadowRes1_2.Disable(); shadowRes1_3.Disable(); shadowRes2.Disable(); shadowRes3.Disable(); //shadowRes4_Limit.Disable(); } private bool fullResVolumeBlur = false; private Patch volumeSars1; private Patch volumeSars2; private Patch volumeSars3; private Patch volumeSars4; private Patch volumeSars5; private Patch volumeSars6; private Patch volumeSars7; private Patch volumeSars8; private Patch volumeSars9; private void fullResVolumeBlurEnable() { volumeSars1.Enable(); volumeSars2.Enable(); volumeSars3.Enable(); volumeSars4.Enable(); volumeSars5.Enable(); volumeSars6.Enable(); volumeSars7.Enable(); volumeSars8.Enable(); volumeSars9.Enable(); } private void fullResVolumeBlurDisable() { volumeSars1.Disable(); volumeSars2.Disable(); volumeSars3.Disable(); volumeSars4.Disable(); volumeSars5.Disable(); volumeSars6.Disable(); volumeSars7.Disable(); volumeSars8.Disable(); volumeSars9.Disable(); } private bool higherVolumeQuality = false; private Patch value2ForVolumeQuality; private bool disableLODLimits = false; private Patch defaultViewModeLODLimit; private Patch defaultViewModeLODLimit_1; private Patch defaultViewModeLODLimit_2; private Patch defaultViewModeLODLimit_3; private Patch defaultViewModeLODLimit_4; private void disableLODLimitsEnable() { defaultViewModeLODLimit.Enable(); defaultViewModeLODLimit_1.Enable(); defaultViewModeLODLimit_2.Enable(); defaultViewModeLODLimit_3.Enable(); defaultViewModeLODLimit_4.Enable(); } private void disableLODLimitsDisable() { defaultViewModeLODLimit.Disable(); defaultViewModeLODLimit_1.Disable(); defaultViewModeLODLimit_2.Disable(); defaultViewModeLODLimit_3.Disable(); defaultViewModeLODLimit_4.Disable(); } private bool largerFoliageSwayRange = false; private Patch addressHigherValueForFoliageSway; private bool disableReducedRateAnimations = false; private Patch zeroFrameSkip; private class Light { public nint Object; public int Type; public Parameter[] Parameters = { new Parameter("Group", 0x13C, ParameterType.INT, false, 1, 1, 3), new Parameter("Position", 0x5A0, ParameterType.VECTOR3, true, 0.1f), new Parameter("Color", 0x140, ParameterType.COLOR, true), new Parameter("Intensity", 0x8A8, ParameterType.FLOAT, true, 0.1f), new Parameter("Radius", 0x6A0, ParameterType.FLOAT, true, 0.25f), new Parameter("Effective Radius", 0x588, ParameterType.FLOAT, true, 0.25f), new Parameter("Min Roughness", 0x58C, ParameterType.FLOAT, true), new Parameter("Shadow Cast", 0x138, ParameterType.FLAG, false, (1 << 23)), new Parameter("Shadow Map Size", 0x1B8, ParameterType.INT, false), // Aligned to 128. new Parameter("Shadow Depth Bias", 0x57C, ParameterType.FLOAT, true, 0.00001f), new Parameter("Shadow Sloped Depth Bias", 0x580, ParameterType.FLOAT, true, 0.00001f), new Parameter("Shadow Max Depth Bias", 0x584, ParameterType.FLOAT, true, 0.00001f), new Parameter("Do Volumetric", 0x590, ParameterType.BOOL, true), new Parameter("Near Clip Distance", 0x1B4, ParameterType.FLOAT, true), //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) { Object = lightObject; Type = type; foreach (Parameter param in Parameters) { param.Update(lightObject); } } public Light(nint lightObject, int type, Vector3 pos) { Object = lightObject; Type = type; foreach (Parameter param in Parameters) { param.OverrideOn(false); } Parameters[0].OverrideValue(default, 3); Parameters[1].OverrideValue(new Vector4(pos.X, pos.Y, pos.Z, 0.0f), 0); Parameters[3].OverrideValue(new Vector4(10.0f, 0.0f, 0.0f, 0.0f), 0); foreach (Parameter param in Parameters) { param.Update(lightObject); } } } // Point Light (2): // Create1: 0x141F875A0 () // Create2: 0x141F87CD0 (nint) // Destroy: 0x141F87D90 (nint, int) // // Spot Light (3): // Create1: 0x141F8AE80 () // Create2: 0x141F8B2D0 (nint) // Destroy: 0x141F8B390 (nint, int) // // Hemi Sphere Light (4): // Create1: 0x141F84840 () // Create2: 0x141F84990 (nint) // Destroy: 0x141F84A40 (nint, int) // // Wrapper? (8): // Create1: 0x141F816C0 () // Create2: 0x141F81800 () // Destroy: 0x141F81940 (nint, int) private int requestLightType = 2; private NativeFunction createLight; private bool interceptCreateLight = false; private List ourLights = new List(); private List sceneLights = new List(); private NativeAction addToScene; private NativeAction removeFromScene; //private delegate nint CreateLightObject(int type); //private Hook? createLightObject; private delegate void DestroyLightObject(nint lightObject, int unknownInt); private Hook? destroyLightObject; private delegate void UpdateLights(nint lightObject); private Hook? updateLights; public int AddOverride(string name, Vector4 v4, int i1) { foreach (Parameter param in allParameters) { if (param.Name == name) { Override ov = new Override(param, v4, i1); Config config = getConfig(); if (!(selectedPreset != "" && unsetIfOverridesContainsParam(config.Presets[selectedPreset], param))) { List? stageOverrides = maybeGetStageOverrides(config); if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, param))) { unsetIfOverridesContainsParam(config.Overrides[globalStage], param); } } ov.Set(); externalId++; externalOverrides.Add(externalId, ov); return externalId; } } return 0; } public void RemoveOverride(int id) { if (externalOverrides.ContainsKey(id)) { Override ovE = externalOverrides[id]; ovE.Unset(); Config config = getConfig(); if (!(selectedPreset != "" && setIfOverridesContainsParam(config.Presets[selectedPreset], ovE.Param!))) { List? stageOverrides = maybeGetStageOverrides(config); if (!(stageOverrides != null && setIfOverridesContainsParam(stageOverrides, ovE.Param!))) { setIfOverridesContainsParam(config.Overrides[globalStage], ovE.Param!); } } externalOverrides.Remove(id); } } public PluginData Initialize() { Instance = this; onAreaChange = Hook.Create(0x141AC27D0, OnAreaChangeHook); // nint // These run during cutscenes. updateSSAOParams = Hook.Create(0x1416D8B10, UpdateSSAOParamsHook); // nint, nint updateSSLRParams = Hook.Create(0x1416DA3F0, UpdateSSLRParamsHook); // nint createFXAAObject = Hook.Create(0x142393560, CreateFXAAObjectHook); createFXAAObject2 = Hook.Create(0x142393680, CreateFXAAObject2Hook); // nint destroyFXAAObject = Hook.Create(0x142393740, DestroyFXAAObjectHook); // nint, int createDofObject = Hook.Create(0x142421E80, CreateDofObjectHook); createDofObject2 = Hook.Create(0x1424220A0, CreateDofObject2Hook); // nint destroyDofObject = Hook.Create(0x142422290, DestroyDofObjectHook); // nint, int updateDofParams = Hook.Create(0x1412BA6A0, UpdateDofParamsHook); // nint createMotionBlurObject = Hook.Create(0x1424C95E0, CreateMotionBlurObjectHook); createMotionBlurObject2 = Hook.Create(0x1424C9740, CreateMotionBlurObject2Hook); // nint destroyMotionBlurObject = Hook.Create(0x1424C97E0, DestroyMotionBlurObjectHook); // nint, int updateMotionBlurParams = Hook.Create(0x1424CADF0, UpdateMotionBlurParamsHook); // nint, nint createBloomObject = Hook.Create(0x1424CB3C0, CreateBloomObjectHook); createBloomObject2 = Hook.Create(0x1424CB5E0, CreateBloomObject2Hook); // nint destroyBloomObject = Hook.Create(0x1424CB750, DestroyBloomObjectHook); // nint, int createLightingObject = Hook.Create(0x1424CDE20, CreateLightingObjectHook); destroyLightingObject = Hook.Create(0x1424CE380, DestroyLightingObjectHook); // nint, int IntPtr addr = PatternScanner.FindFirst(Pattern.FromString("48 89 5C 24 10 48 89 74 24 18 48 89 7C 24 20 55 41 56 41 57 48 8B EC 48 83 EC 30 48 8B FA 48 8B D9 E8 AA B6 C1 FF 48 8B 47 10 48 8D 57 50")); Assert(addr == 0x141AB2260); // nint, nint updateShadowParams = Hook.Create(addr, UpdateShadowParamsHook); staticShadowParams = Hook.Create(0x1416D94F0, StaticShadowParamsHook); // nint, nint setShadowQuality = new NativeAction(0x14043FF80); 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); Assert(MemoryUtil.Read(0x142E4FE5C) == 3.0f); Assert(MemoryUtil.Read(0x142C4A2F0) == 4.0f); shadowRes1_3x = new Patch(addr + 0x8, [0xD0, 0xFE, 0xA0, 0x02]); // 3.0. shadowRes1_4x = new Patch(addr + 0x8, [0x64, 0xA3, 0x80, 0x02]); // 4.0. shadowRes1_1 = new Patch(addr + 0x65, [0x17]); // Always set "Value is not 1.0" flag. shadowRes1_2 = new Patch(addr + 0x68, [0x59]); // movss -> mulss. shadowRes1_3 = new Patch(addr + 0x77, [0x59]); // movss -> mulss. // shadowRes2/3 control the detail of the infrequently updated fallback shadows (Broad Area). addr = PatternScanner.FindFirst(Pattern.FromString("83 FA FF 74 06 89 91 1C 55 00 00 8B 91 30 55 00 00 83 FA FF 7F 06 8B 91 1C 55 00 00 85 D2 74 4B 83 EA 01 74 3B 83 EA 01 74 2B 83 EA 01 74 1B 83 FA 01 74 0B C7 81 20 55 00 00 01 00 00 00 C3 C7 81 20 55 00 00 00 10 00 00")); Assert(addr == 0x142287A50); shadowRes2 = new Patch(addr + 0x3F, [ 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC3, // High (8192x8192). 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0xC3, // Mid (6144x6144). 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0xC3 // Low (4096x4096). ]); addr = PatternScanner.FindFirst(Pattern.FromString("89 91 30 55 00 00 83 FA FF 7F 06 8B 91 1C 55 00 00 85 D2 74 4B 83 EA 01 74 3B 83 EA 01 74 2B 83 EA 01 74 1B 83 FA 01 74 0B C7 81 20 55 00 00 01 00 00 00 C3 C7 81 20 55 00 00 00 10 00 00")); Assert(addr == 0x142287AD0); shadowRes3 = new Patch(addr + 0x34, [ 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0xC3 ]); addr = PatternScanner.FindFirst(Pattern.FromString("89 91 00 55 00 00 83 FA 05 77 41 48 63 C2 4C 8D 05 ?? ?? ?? ?? 41 8B 94 80 84 8B 28 02 49 03 D0 FF E2 B8 00 08 00 00")); Assert(addr == 0x142288B00); // This value controls the shadow map texture resolution. 16384x16384 is likely the limit for many drivers // and any value higher would crash. Keeping this at the default value (unpatched) *should* work unless the // game does something unpredictable. At 3x Shadow Resolution + High, the shadow map will be 8448x8448. /* At 3x Shadow Resolution, results in a 16320x16320 shadow map. Adjusting the shadow map resolution via this * value feels unsafe, especially if the result is not a multiple of something the game would produce. shadowRes4_Limit = new Patch(addr + 0x29, [ // N/64 = 255, fractional values closer to 256 not tested. 0xB8, 0x40, 0x15, 0x00, 0x00, 0xEB, 0x21 // This case is used for Low, Medium and High in-game. ]); */ // The volume blur filter is created at a different place in the code and happens at a different // time (area change/createLightingObject() vs change of Volume Quality setting). It's also rendered // in a seperate pass that happens after the initial volume rendering. So keep that in mind if inspecting // a frame in renderdoc. volumeSars1 = new Patch(new IntPtr(0x1424D0159), [0x90, 0x90]); volumeSars2 = new Patch(new IntPtr(0x1424D01A0), [0x90, 0x90]); volumeSars3 = new Patch(new IntPtr(0x1424D01C6), [0x90, 0x90]); volumeSars4 = new Patch(new IntPtr(0x1424D0219), [0x90, 0x90]); volumeSars5 = new Patch(new IntPtr(0x1424D0242), [0x90, 0x90]); volumeSars6 = new Patch(new IntPtr(0x1424D029D), [0x90, 0x90]); volumeSars7 = new Patch(new IntPtr(0x1424D02C6), [0x90, 0x90]); volumeSars8 = new Patch(new IntPtr(0x1424D0321), [0x90, 0x90]); volumeSars9 = new Patch(new IntPtr(0x1424D034A), [0x90, 0x90]); /* As far as I can tell this is limited by buffers created cpu-side, seems like it * would be an extremely hard change. froxelRes1 = new Patch(new IntPtr(0x142384F15) + 0x1, [0x02]); // 4 -> 2 froxelRes2 = new Patch(new IntPtr(0x142389711) + 0x1, [0x02]); // 4 -> 2 froxelRes3 = new Patch(new IntPtr(0x142389878) + 0x1, [0x02]); // 4 -> 2 froxelRes4 = new Patch(new IntPtr(0x14238989E) + 0x1, [0x02]); // 4 -> 2 froxelRes5 = new Patch(new IntPtr(0x1423898D8) + 0x1, [0x02]); // 4 -> 2 froxelRes6 = new Patch(new IntPtr(0x14238992B) + 0x1, [0x02]); // 4 -> 2 froxelRes7 = new Patch(new IntPtr(0x14238FED1) + 0x4, [0x7B, 0xFF, 0xAB, 0x00]); // 0.25 -> 0.50 froxelRes8 = new Patch(new IntPtr(0x14239009C) + 0x4, [0xB0, 0xFD, 0xAB, 0X00]); // 0.25 -> 0.50 */ /* Would require a complicated shader edit to maybe work. Similar to SSLR. checkerRes1 = new Patch(new IntPtr(0x14238580D) + 0x4, [0x43]); // 0.5 -> 1.0 checkerRes2 = new Patch(new IntPtr(0x14238FFC3) + 0x4, [0x8D]); // 0.5 -> 1.0 */ value2ForVolumeQuality = new Patch(new IntPtr(0x142389652), [0xB8, 0x02, 0x00, 0x00, 0x00, 0x90]); // Gameplay. addr = PatternScanner.FindFirst(Pattern.FromString("80 BB 34 EC 00 00 00 88 8B 31 EC 00 00 C6 83 30 EC 00 00 00 74 1C C6 83 34 EC 00 00 00 48 8B 0D 68 4B 4F 03 80 B9 58 02 00 00 00 75 05 E8 5A F5 01 00 48 8B CB E8 E2 05 00 00 83 BB DC EB 00 00 06 74 0A C7 83 DC EB 00 00 06 00 00 00")); Assert(addr == 0x141B1A0B4); defaultViewModeLODLimit = new Patch(addr + 0x40, [0x07, 0x74, 0x0A, 0xC7, 0x83, 0xDC, 0xEB, 0x00, 0x00, 0x07]); // In room. addr = PatternScanner.FindFirst(Pattern.FromString("8B 9F CC 00 00 00 81 FB F9 01 00 00 74 0C 8B CB E8 95 3F 65 01 83 F8 05 75 11 48 8B 0D 61 C8 F6 04 BA 02 00 00 00 E8 DF 2D 8C 01 48 8B 0D A0 C7 F6 04 8B D3")); Assert(addr == 0x140257AE6); defaultViewModeLODLimit_1 = new Patch(addr + 0x22, [0x07]); // Forging/Changing Equipment. addr = PatternScanner.FindFirst(Pattern.FromString("BA 02 00 00 00 E8 6E C0 80 00 48 8B 74 24 38 48 8B 57 28 48 8D 4F 30 E8 4C 79 B4 FE 48 8B 0D 0D FF A6 03 BA 04 00 00 00 E8 8B 64 3C 00 48 8B 05 D4 01 A7 03 80 B8 FD 46 01 00 00")); Assert(addr == 0x141754438); defaultViewModeLODLimit_2 = new Patch(addr + 0x24, [0x07]); // Unknown. defaultViewModeLODLimit_3 = new Patch(new IntPtr(0x14128F1AC) + 0x1, [0x07]); // Talking to NPC. defaultViewModeLODLimit_4 = new Patch(new IntPtr(0x14128F1AC) + 0xF, [0x07]); addr = PatternScanner.FindFirst(Pattern.FromString("74 4D 33 C0 48 81 C1 00 02 00 00 F3 0F 10 11 0F 2F CA 72 0C FF C0 48 83 C1 04 83 F8 03 72 EC C3")); Assert(addr == 0x1426D89CA); addressHigherValueForFoliageSway = new Patch(addr + 0x7, [0x04]); // Found using mFrameSkipNum annotation from MHW-DTI-Dumps. zeroFrameSkip = new Patch(new IntPtr(0x142246948), [0x31, 0xC0, 0x90, 0x90, 0x90, 0x90]); // The game will crash when trying to switch to the FULL_RES path for SSR. It's likley because // it tries to access resources (shader, buffer, texture, etc.) that are not loaded. // Ex: MonsterHunterWorld.exe+228C4A7 - mov rdi,[rdi+00000120] # Attempted read. // MonsterHunterWorld.exe+259329B - lea rcx,[rdi+00000120] # Zero. // The code that zero's these addresses looks like a shell of where they would be loaded. So my assumption // is that it's compiled out and that this method of increasing the SSR resolution will not work. // Though, it may still be possible by finagling the parameters of the half res case. /* // Attempt to forcefully enable FULL_RES path for SSR. ssrRes1 = new Patch((nint)0x14228894D, [0x0F, 0x85, 0xB9, 0xAA, 0x30, 0x00]); ssrRes1_1 = new Patch((nint)0x14259340C, [0xC6, 0x81, 0x28, 0x02, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90]); ssrRes2 = new Patch((nint)0x14228897D, [0x0F, 0x85, 0x99, 0xAA, 0x30, 0x00]); ssrRes2_1 = new Patch((nint)0x14259341C, [0xC6, 0x81, 0x30, 0x02, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90]); ssrRes3 = new Patch((nint)0x1422889AD, [0x0F, 0x85, 0x79, 0xAA, 0x30, 0x00]); ssrRes3_1 = new Patch((nint)0x14259342C, [0xC6, 0x81, 0x2C, 0x02, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90]); ssrRes4 = new Patch((nint)0x14259268C, [0x41, 0xC7, 0x86, 0x28, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xC7, 0x86, 0x2C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xC7, 0x86, 0x30, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0xC7, 0x86, 0x34, 0x02, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x41, 0xC7, 0x86, 0x38, 0x02, 0x00, 0x00, 0x00, 0x00, 0x80, 0x3F, 0x41, 0xC6, 0x86, 0x3C, 0x02, 0x00, 0x00, 0x01]); */ // None of these work either. /* // Don't scale resolution by SSR Factor. ssrRes10 = new Patch((nint)0x142592DEF, [0x90, 0x90, 0x90, 0x90]); ssrRes11 = new Patch((nint)0x142592E00, [0x90, 0x90, 0x90, 0x90]); // Make SSR Factor 1.0. ssrRes12 = new Patch((nint)0x1425926B8 + 0x7, [0x00, 0x00, 0x80, 0x3F]); */ /* Double width and height (3rd and 4th parameter). initSSRHook = Hook.Create(0x1425924D5, InitSSRHook); // nint, nint, int, int increaseSSRMipSize1 = new Patch((nint)0x142592EBA + 0x2, [0x2]); increaseSSRMipSize2 = new Patch((nint)0x142592ED0 + 0x2, [0x2]); */ createLight = new NativeFunction(0x1418A3EF0); //createLightObject = Hook.Create(0x1418A3EF0, CreateLightObjectHook); destroyLightObject = Hook.Create(0x141F87D90, DestroyLightObjectHook); addToScene = new NativeAction(0x142219070); removeFromScene = new NativeAction(0x142228F10); updateLights = Hook.Create(0x141F88F30, UpdateLightsHook); return new PluginData(); } public void OnPreMain() { Config config = getConfig(); Config.PatchConfig patches = config.Patches; tripleShadowResolution = patches.TripleShadowResolution; if (tripleShadowResolution) { tripleShadowResEnable(); } fullResVolumeBlur = patches.FullResolutionVolumeBlur; if (fullResVolumeBlur) { fullResVolumeBlurEnable(); } higherVolumeQuality = patches.HigherThanHighestVolumeRendering; if (higherVolumeQuality) { value2ForVolumeQuality.Enable(); } disableLODLimits = patches.DisableLODLimits; if (disableLODLimits) { disableLODLimitsEnable(); } largerFoliageSwayRange = patches.LargerFoliageSwayRange; if (largerFoliageSwayRange) { addressHigherValueForFoliageSway.Enable(); } disableReducedRateAnimations = patches.DisableReducedRateAnimations; if (disableReducedRateAnimations) { zeroFrameSkip.Enable(); } } public void OnLoad() { sMain = SingletonManager.GetSingleton("sMhMain")!; sMhScene = SingletonManager.GetSingleton("sMhScene")!; sLightProbes = SingletonManager.GetSingleton("sLightProbes")!; sLightLinker = SingletonManager.GetSingleton("sLightLinker")!; sMhSpeedTree = SingletonManager.GetSingleton("sMhSpeedTree")!; Config config = getConfig(); foreach (Stage area in Enum.GetValues(typeof(Stage))) { if (!config.Overrides.ContainsKey(area)) { config.Overrides.Add(area, new List()); } } foreach (Override ovG in config.Overrides[globalStage]) { ovG.Set(); } ConfigManager.SaveConfig(this); } /* 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; public void OnResourceLoad(Resource? resource, MtDti dti, string path, LoadFlags flags) { if (adjustCtcParams && resource != null && resource.FileExtension == "ctc") { Log.Info($"Attempting to increase physics update rate of {resource.FilePath} @ {resource.Instance:X}."); int numARecords = MemoryUtil.GetRef(resource.Instance + 0xB8); nint aRecords = MemoryUtil.GetRef(resource.Instance + 0xF8); for (int i = 0; i < numARecords; i++) { nint aRecord = aRecords + (0x50 * i); } MemoryUtil.GetRef(resource.Instance + 0xC4) = 1.0f / stepTimeFps; } } */ public void OnUpdate(float deltaTime) { if (fxaaObject != 0x0) { foreach (Parameter param in fxaaParameters) { param.Update(fxaaObject); } } if (dofObject != 0x0) { foreach (Parameter param in dofParameters) { if (!param.PerFrame) { param.Update(dofObject); } } } if (motionBlurObject != 0x0) { foreach (Parameter param in motionBlurParameters) { if (!param.PerFrame) { param.Update(motionBlurObject); } } } if (bloomObject != 0x0) { foreach (Parameter param in bloomParameters) { if (!param.PerFrame) { param.Update(bloomObject); } } } if (lightingObject != 0x0) { foreach (Parameter param in lightingParameters) { if (!param.PerFrame) { param.Update(lightingObject); } } } if (sLightProbes!.Instance != 0x0) { foreach (Parameter param in lightProbesParameters) { param.Update(sLightProbes!.Instance); } } if (sMhScene!.Instance != 0x0) { foreach (Parameter param in ssaoParameters) { if (!param.PerFrame) { param.Update(sMhScene!.Instance); } } foreach (Parameter param in sslrParameters) { if (!param.PerFrame) { param.Update(sMhScene!.Instance); } } foreach (Parameter param in sceneParameters) { if (!param.PerFrame) { param.Update(sMhScene!.Instance); } } } } private void UpdateSSAOParamsHook(nint sceneObjectInternal, nint stackOffset) { updateSSAOParams!.Original(sceneObjectInternal, stackOffset); foreach (Parameter param in ssaoParameters) { param.Update(sMhScene!.Instance); } } private void UpdateSSLRParamsHook(nint stackOffset) { updateSSLRParams!.Original(stackOffset); foreach (Parameter param in sslrParameters) { param.Update(sMhScene!.Instance); } } private nint CreateFXAAObjectHook() { fxaaObject = createFXAAObject!.Original(); foreach (Parameter param in fxaaParameters) { param.Update(fxaaObject); } return fxaaObject; } private nint CreateFXAAObject2Hook(nint unknownPtr) { fxaaObject = createFXAAObject2!.Original(unknownPtr); foreach (Parameter param in fxaaParameters) { param.Update(fxaaObject); } return fxaaObject; } private nint DestroyFXAAObjectHook(nint fxaaObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (fxaaObject == fxaaObjectInternal) { fxaaObject = 0x0; foreach (Parameter param in fxaaParameters) { param.Update(fxaaObject); } } return destroyFXAAObject!.Original(fxaaObjectInternal, unknownInt); } private nint CreateDofObjectHook() { dofObject = createDofObject!.Original(); foreach (Parameter param in dofParameters) { param.Update(dofObject); } return dofObject; } private nint CreateDofObject2Hook(nint unknownPtr) { dofObject = createDofObject2!.Original(unknownPtr); foreach (Parameter param in dofParameters) { param.Update(dofObject); } return dofObject; } private nint DestroyDofObjectHook(nint dofObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (dofObject == dofObjectInternal) { dofObject = 0x0; foreach (Parameter param in dofParameters) { param.Update(dofObject); } } return destroyDofObject!.Original(dofObjectInternal, unknownInt); } private void UpdateDofParamsHook(nint unknownPtr) { updateDofParams!.Original(unknownPtr); foreach (Parameter param in dofParameters) { if (param.PerFrame) { param.Update(dofObject); } } } private nint CreateMotionBlurObjectHook() { motionBlurObject = createMotionBlurObject!.Original(); foreach (Parameter param in motionBlurParameters) { param.Update(motionBlurObject); } return motionBlurObject; } private nint CreateMotionBlurObject2Hook(nint unknownPtr) { motionBlurObject = createMotionBlurObject2!.Original(unknownPtr); foreach (Parameter param in motionBlurParameters) { param.Update(motionBlurObject); } return motionBlurObject; } private nint DestroyMotionBlurObjectHook(nint motionBlurObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (motionBlurObject == motionBlurObjectInternal) { motionBlurObject = 0x0; foreach (Parameter param in motionBlurParameters) { param.Update(motionBlurObject); } } return destroyMotionBlurObject!.Original(motionBlurObjectInternal, unknownInt); } private void UpdateMotionBlurParamsHook(nint unknownPtr, nint unknownPtr2) { foreach (Parameter param in motionBlurParameters) { if (param.PerFrame) { param.Update(motionBlurObject); } } updateMotionBlurParams!.Original(unknownPtr, unknownPtr2); } private nint CreateBloomObjectHook() { bloomObject = createBloomObject!.Original(); foreach (Parameter param in bloomParameters) { param.Update(bloomObject); } return bloomObject; } private nint CreateBloomObject2Hook(nint unknownPtr) { bloomObject = createBloomObject2!.Original(unknownPtr); foreach (Parameter param in bloomParameters) { param.Update(bloomObject); } return bloomObject; } private nint DestroyBloomObjectHook(nint bloomObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (bloomObject == bloomObjectInternal) { bloomObject = 0x0; foreach (Parameter param in bloomParameters) { param.Update(bloomObject); } } return destroyBloomObject!.Original(bloomObjectInternal, unknownInt); } private nint CreateLightingObjectHook() { lightingObject = createLightingObject!.Original(); foreach (Parameter param in lightingParameters) { param.Update(lightingObject); } return lightingObject; } private nint DestroyLightingObjectHook(nint lightingObjectInternal, int unknownInt) { Assert(unknownInt == 1); if (lightingObject == lightingObjectInternal) { lightingObject = 0x0; foreach (Parameter param in lightingParameters) { param.Update(lightingObject); } } return destroyLightingObject!.Original(lightingObjectInternal, unknownInt); } private void UpdateShadowParamsHook(nint shadowObjectInternal, nint stackOffset) { nint shadowCascadeMode = MemoryUtil.GetRef(stackOffset + 0x170) >> 32; bool firstPass = shadowCascadeMode == 3; if (!firstPass) { foreach (Parameter param in shadowParameters2) { param.Update(stackOffset); } } updateShadowParams!.Original(shadowObjectInternal, stackOffset); } private void StaticShadowParamsHook(nint shadowObjectStatic, nint shadowObjectInternal) { if (shadowObjectInternal == 0x0) { foreach (Parameter param in shadowParameters2) { param.Update(shadowObjectStatic); } } staticShadowParams!.Original(shadowObjectStatic, shadowObjectInternal); } /* private nint CreateLightObjectHook(int type) { nint lightObject = createLightObject!.Original(type); if (interceptCreateLight) { interceptCreateLight = false; } //else if (type == 2) else { sceneLights.Add(new Light(lightObject, type)); } return lightObject; } */ private void DestroyLightObjectHook(nint lightObject, int unknownInt) { for (int i = 0; i < sceneLights.Count; i++) { if (lightObject == sceneLights[i].Object) { sceneLights.RemoveAt(i); break; } } for (int i = 0; i < ourLights.Count; i++) { if (lightObject == ourLights[i].Object) { ourLights.RemoveAt(i); break; } } destroyLightObject!.Original(lightObject, unknownInt); } private void UpdateLightsHook(nint lightObject) { Light? light = null; for (int i = 0; i < sceneLights.Count; i++) { if (lightObject == sceneLights[i].Object) { light = sceneLights[i]; break; } } if (light == null) { for (int i = 0; i < ourLights.Count; i++) { if (lightObject == ourLights[i].Object) { light = ourLights[i]; break; } } } updateLights!.Original(lightObject); if (light != null) { foreach (Parameter param in light.Parameters) { param.Update(lightObject); } } } private void OnAreaChangeHook(nint unknownPtr) { 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) { globalOverrides = config.Overrides[globalStage]; } foreach (Override ovR in prevOverrides) { // 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) { ovR.Unset(); if (globalOverrides != null && ovR.Param != null) { setIfOverridesContainsParam(globalOverrides, ovR.Param); } } } if (globalOverrides != null) // Stage isn't global. { List stageOverrides = config.Overrides[stage]; foreach (Override ovS in stageOverrides) { bool superseded = stageOverrideSuperseded(config, ovS.Param); if (!superseded) { if (ovS.Param != null) { unsetIfOverridesContainsParam(globalOverrides, ovS.Param); } ovS.Set(); } } } } public void OnImGuiRender() { 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))) { foreach (Stage area in Enum.GetValues(typeof(Stage))) { string name = Config.StageToString(area); bool isSelected = selectedStage == area; if (ImGui.Selectable(name, isSelected)) { selectedStage = area; } if (isSelected) ImGui.SetItemDefaultFocus(); } ImGui.EndCombo(); } ImGui.SameLine(); if (ImGui.Button("Current")) { if (currentStage != 0) { selectedStage = currentStage; } else { selectedStage = globalStage; } } ImGui.SameLine(); if (ImGui.Button("Global")) { selectedStage = globalStage; } Config config = getConfig(); List selectedOverrides = config.Overrides[selectedStage]; List? stageOverrides = maybeGetStageOverrides(config); List? globalOverrides = null; if (selectedStage != globalStage) { globalOverrides = config.Overrides[globalStage]; } 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(); for (int i = 0; i < selectedOverrides.Count; i++) { ImGui.PushID(i); Override ovS = selectedOverrides[i]; bool superseded = selectedOverrideSuperseded(config, globalOverrides, stageOverrides, ovS.Param); bool requestRemove = ImGui.Button("X"); ImGui.SameLine(); bool requestUp = ImGui.Button("▲"); ImGui.SameLine(); bool requestDown = ImGui.Button("▼"); ImGui.SameLine(); if (requestDown && i < selectedOverrides.Count - 1) { (selectedOverrides[i], selectedOverrides[i + 1]) = (selectedOverrides[i + 1], selectedOverrides[i]); } ovS.Draw(selectedOverrides, null, globalOverrides, superseded, width); if (requestUp && i > 0) { (selectedOverrides[i], selectedOverrides[i - 1]) = (selectedOverrides[i - 1], selectedOverrides[i]); } if (requestRemove) { if (!superseded) { ovS.Unset(); if (ovS.Param != null && globalOverrides != null) { setIfOverridesContainsParam(globalOverrides, ovS.Param); } } selectedOverrides.RemoveAt(i); i--; } ImGui.PopID(); } if (ImGui.Button("Save")) { for (int i = 0; i < selectedOverrides.Count; i++) { Override ovS = selectedOverrides[i]; if (ovS.Param == null) { selectedOverrides.RemoveAt(i); i--; continue; } ovS.Save(); } ConfigManager.SaveConfig(this); } ImGui.Separator(); // Global overrides and presets could now be show at the same time. globalOverrides = config.Overrides[globalStage]; ImGui.PushID("Preset"); ImGui.Text("Quick Presets:"); ImGui.PushItemWidth(width * 0.35f); if (ImGui.Button("+")) { string presetName = "New Set"; int presetDup = 0; while (config.Presets.ContainsKey(presetName)) { presetDup++; presetName = $"New Set ({presetDup})"; } config.Presets[presetName] = new List(); selectedPreset = presetName; } ImGui.SameLine(); if (renamingPreset) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } string prevPreset = selectedPreset; if (ImGui.BeginCombo("##Presets", selectedPreset)) { if (ImGui.Selectable("(None)", false)) { selectedPreset = ""; } foreach ((string name, List overrides) in config.Presets) { bool isSelected = name == selectedPreset; if (ImGui.Selectable(name, isSelected)) { selectedPreset = name; } } ImGui.EndCombo(); } 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(); } } } } if (renamingPreset) { ImGui.PopStyleVar(); ImGui.PopItemFlag(); } ImGui.PopItemWidth(); if (selectedPreset != "") { ImGui.SameLine(); ImGui.PushItemWidth(width * 0.2f); if (renamingPreset) { ImGui.InputText("##Preset Name", ref typedPresetName, 99); ImGui.SameLine(); if (typedPresetName == "") { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (ImGui.Button("Save")) { string presetName = typedPresetName; if (presetName != selectedPreset) { int presetDup = 0; while (config.Presets.ContainsKey(presetName)) { presetDup++; presetName = $"{typedPresetName} ({presetDup})"; } List tmpOverrides = config.Presets[selectedPreset]; config.Presets.Remove(selectedPreset); config.Presets[presetName] = tmpOverrides; selectedPreset = presetName; } renamingPreset = false; } if (typedPresetName == "") { ImGui.PopStyleVar(); ImGui.PopItemFlag(); } } else if (ImGui.Button("Rename")) { renamingPreset = true; typedPresetName = selectedPreset; } ImGui.PopItemWidth(); List presetOverrides = config.Presets[selectedPreset]; if (ImGui.Button("Add override")) { Override ov = new Override(); ov.Set(); // Empty override never superseded. presetOverrides.Add(ov); } for (int i = 0; i < presetOverrides.Count; i++) { ImGui.PushID(i); Override ovP = presetOverrides[i]; bool superseded = ovP.Param != null && overridesContainsParam(externalOverrides.Values, ovP.Param); bool requestRemove = ImGui.Button("X"); ImGui.SameLine(); bool requestUp = ImGui.Button("▲"); ImGui.SameLine(); bool requestDown = ImGui.Button("▼"); ImGui.SameLine(); if (requestDown && i < presetOverrides.Count - 1) { (presetOverrides[i], presetOverrides[i + 1]) = (presetOverrides[i + 1], presetOverrides[i]); } ovP.Draw(presetOverrides, stageOverrides, globalOverrides, superseded, width); if (requestUp && i > 0) { (presetOverrides[i], presetOverrides[i - 1]) = (presetOverrides[i - 1], presetOverrides[i]); } if (requestRemove) { if (!superseded) { ovP.Unset(); if (ovP.Param != null) { if (!(stageOverrides != null && setIfOverridesContainsParam(stageOverrides, ovP.Param))) { setIfOverridesContainsParam(globalOverrides, ovP.Param); } } } presetOverrides.RemoveAt(i); i--; } ImGui.PopID(); } if (ImGui.Button("Save")) { for (int i = 0; i < presetOverrides.Count; i++) { Override ovP = presetOverrides[i]; if (ovP.Param == null) { presetOverrides.RemoveAt(i); i--; continue; } ovP.Save(); } ConfigManager.SaveConfig(this); } } ImGui.PopID(); ImGui.Separator(); ImGui.PushID("External"); ImGui.Text("External:"); foreach (Override ovE in externalOverrides.Values) { ovE.Draw(null, null, null, false, width); } ImGui.PopID(); ImGui.Separator(); if (ImGui.CollapsingHeader("Patches")) { Config.PatchConfig patches = config.Patches; if (ImGui.Checkbox("3x Shadow Resolution", ref tripleShadowResolution)) { if (tripleShadowResolution) { tripleShadowResEnable(); } else { tripleShadowResDisable(); } setShadowQuality.Invoke(MemoryUtil.Read(sMhScene!.Instance + 0x5530) + 1); patches.TripleShadowResolution = tripleShadowResolution; config.Patches = patches; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("In-Game Settings:\n - High: 1.0 -> 3.0\n - Mid: 0.7 -> 2.1\n - Low: 0.5 -> 1.5"); ImGui.EndTooltip(); } ImGui.SameLine(); if (ImGui.Checkbox("4x Shadow Resolution", ref quadrupleShadowResolution)) { if (quadrupleShadowResolution) { shadowRes1_3x.Disable(); shadowRes1_4x.Enable(); } else { shadowRes1_4x.Disable(); shadowRes1_3x.Enable(); } setShadowQuality.Invoke(MemoryUtil.Read(sMhScene!.Instance + 0x5530) + 1); } if (ImGui.Checkbox("Volume Rendering Full Resolution Blur Pass (Requires Area Change)", ref fullResVolumeBlur)) { if (fullResVolumeBlur) { fullResVolumeBlurEnable(); } else { fullResVolumeBlurDisable(); } patches.FullResolutionVolumeBlur = fullResVolumeBlur; config.Patches = patches; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("This can get rid of excessive aliasing when something is in front of a volumetric effect.\nMove to a different area for this to apply, it will stay applied after that."); ImGui.EndTooltip(); } if (ImGui.Checkbox("Higher Than \"Highest\" Volume Rendering Quality", ref higherVolumeQuality)) { if (higherVolumeQuality) { value2ForVolumeQuality.Enable(); } else { value2ForVolumeQuality.Disable(); } patches.HigherThanHighestVolumeRendering = higherVolumeQuality; config.Patches = patches; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("*Extremely Resource Intensive* Only noticeable effect is likely in areas that had visible breakup with Volume Rendering Quality: Highest.\nThis takes mostly the same path as Highest."); ImGui.EndTooltip(); } if (ImGui.Checkbox("Disable Player/Palico/NPC LOD Limit in Gameplay", ref disableLODLimits)) { if (disableLODLimits) { disableLODLimitsEnable(); } else { disableLODLimitsDisable(); } patches.DisableLODLimits = disableLODLimits; config.Patches = patches; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("This will make the default (gameplay) LOD limits equivalent to View Mode. Disabling the limit on:\n - Player Models & Shadows\n - Palico Models & Shadows\n - NPC Models & Shadows\n - Simple NPC Models & Shadows\nAlso equivalent to the settings applied in your room with the addition of disabled Simple NPC limits."); ImGui.EndTooltip(); } if (ImGui.Checkbox("Larger Foliage Sway Range", ref largerFoliageSwayRange)) { if (largerFoliageSwayRange) { addressHigherValueForFoliageSway.Enable(); } else { addressHigherValueForFoliageSway.Disable(); } patches.LargerFoliageSwayRange = largerFoliageSwayRange; config.Patches = patches; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Increase the distance at which Foliage Sway is still applied to lower priority plants."); ImGui.EndTooltip(); } if (ImGui.Checkbox("Disable Reduced Rate Animations", ref disableReducedRateAnimations)) { if (disableReducedRateAnimations) { zeroFrameSkip.Enable(); } else { zeroFrameSkip.Disable(); } patches.DisableReducedRateAnimations = disableReducedRateAnimations; config.Patches = patches; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Ignore the frame skip set on an animal's animation when are far from the camera."); ImGui.EndTooltip(); } } if (ImGui.CollapsingHeader("Lights")) { /* ImGui.NextItemWidth(width * 0.25f); ImGui.InputInt("Type", ref requestLightType, 1); ImGui.SameLine(); */ if (ImGui.Button("New")) { interceptCreateLight = true; nint lightObject = createLight.Invoke(requestLightType); Vector3 pos = default; Player? player = Player.MainPlayer; if (player != null) { pos = player.Position; pos.Y += 30.0f; } ourLights.Add(new Light(lightObject, requestLightType, pos)); // mov byte ptr[rsp+20],00 unaccounted for, hopefully it never matters. addToScene.Invoke(MemoryUtil.Read(0x1451238C8), 0x16, lightObject, 0x0); } ImGui.Separator(); ImGui.PushID("Ours"); for (int i = 0; i < ourLights.Count; i++) { Light light = ourLights[i]; ImGui.PushID(i); ImGui.Text($"Type: {light.Type}, Address: {light.Object:X}"); foreach (Parameter param in light.Parameters) { param.Draw(width, true); } /* if (ImGui.CollapsingHeader("Random Move")) { foreach (Parameter param in light.RandomMoveParameters) { param.Draw(width, true); } } */ if (ImGui.Button("Remove")) { /* nint vTable = MemoryUtil.Read(lightObject); NativeAction freeLight = new NativeAction(MemoryUtil.Read(vTable)); freeLight.Invoke(lightObject, 1); */ // removeFromScene triggers destructor. removeFromScene.Invoke(light.Object); } ImGui.PopID(); ImGui.Separator(); } ImGui.PopID(); ImGui.PushID("Scene"); /* if (ImGui.CollapsingHeader("Scene Lights")) { for (int i = 0; i < sceneLights.Count; i++) { Light light = sceneLights[i]; ImGui.PushID(i); ImGui.Text($"Type: {light.Type}, Address: {light.Object:X}"); foreach (Parameter param in light.Parameters) { param.Draw(width); } if (ImGui.Button("Remove")) { removeFromScene.Invoke(light.Object); } ImGui.PopID(); ImGui.Separator(); } } */ ImGui.PopID(); } if (ImGui.CollapsingHeader("Parameters")) { ImGui.Separator(); if (ImGui.CollapsingHeader("Scene")) { ImGui.PushID("Scene"); ImGui.Text($"Address: {sMhScene!.Instance:X}"); foreach (Parameter param in sceneParameters) { param.Draw(width); } ImGui.PopID(); } if (ImGui.CollapsingHeader("Sun Light/Shadows")) { ImGui.PushID("Shadows"); // MonsterHunterWorld.exe+1AB99E0 - mov rax,[MonsterHunterWorld.exe+500E180] nint shadowAddr = MemoryUtil.Read(0x14500E180); nint shadowObject = MemoryUtil.Read(shadowAddr + 0x4B0); shadowAddr += 0x5B0; ImGui.Text($"Address: {shadowAddr:X}, Object: {shadowObject:X}"); /* if (ImGui.CollapsingHeader("Pass 1")) { ImGui.PushID("Pass1"); foreach (Parameter param in shadowParameters1) { param.Draw(width); } ImGui.PopID(); } if (ImGui.CollapsingHeader("Pass 2")) { ImGui.PushID("Pass2"); */ foreach (Parameter param in shadowParameters2) { param.Draw(width); } /* ImGui.PopID(); } */ ImGui.PopID(); } if (ImGui.CollapsingHeader("Screen-space Ambient Occlusion")) { ImGui.PushID("SSAO"); ImGui.Text($"Address: {sMhScene!.Instance:X}"); foreach (Parameter param in ssaoParameters) { param.Draw(width); } ImGui.PopID(); } if (ImGui.CollapsingHeader("Screen-space Local Reflections")) { ImGui.PushID("SSLR"); ImGui.Text($"Address: {sMhScene!.Instance:X}"); foreach (Parameter param in sslrParameters) { param.Draw(width); } ImGui.PopID(); } if (ImGui.CollapsingHeader("Bloom")) { ImGui.PushID("Bloom"); ImGui.Text($"Address: {bloomObject:X}"); if (bloomObject != 0x0) { foreach (Parameter param in bloomParameters) { param.Draw(width); } } } if (ImGui.CollapsingHeader("Depth of Field")) { ImGui.PushID("DepthOfField"); ImGui.Text($"Address: {dofObject:X}"); if (dofObject != 0x0) { foreach (Parameter param in dofParameters) { param.Draw(width); } } ImGui.PopID(); } if (ImGui.CollapsingHeader("Motion Blur")) { ImGui.PushID("MotionBlur"); ImGui.Text($"Address: {motionBlurObject:X}"); if (motionBlurObject != 0x0) { foreach (Parameter param in motionBlurParameters) { param.Draw(width); } } } if (ImGui.CollapsingHeader("FXAA")) { ImGui.PushID("FXAA"); ImGui.Text($"Address: {fxaaObject:X}"); if (fxaaObject != 0x0) { foreach (Parameter param in fxaaParameters) { param.Draw(width); } } ImGui.PopID(); } if (ImGui.CollapsingHeader("Lighting")) { ImGui.PushID("Lighting"); ImGui.Text($"Address: {lightingObject:X}"); if (lightingObject != 0x0) { foreach (Parameter param in lightingParameters) { param.Draw(width); } } ImGui.PopID(); } if (ImGui.CollapsingHeader("Light Probes")) { ImGui.PushID("LightProbes"); ImGui.Text($"Address: {sLightProbes!.Instance:X}"); foreach (Parameter param in lightProbesParameters) { param.Draw(width); } ImGui.PopID(); } ImGui.Separator(); } if (ImGui.BeginItemTooltip()) { ImGui.Text("\"Original\" values may be inaccurate for multiple reasons, including\n 1. The value was set by the game to the same value as an already applied Override.\n 2. The value is being set per-frame in a location unknown to this mod."); ImGui.EndTooltip(); } if (ImGui.CollapsingHeader("DEBUG")) { ImGui.PushItemWidth(width * 0.15f); ImGui.Text($"FPS: {MemoryUtil.GetRef(sMain!.Instance + 0x68)}"); ImGui.Text($"Delta Time: {MemoryUtil.GetRef(sMain!.Instance + 0x94)}"); // Locations taken from MHW-DTI-Dumps/wip_dump_15_20_00.h. ImGui.InputFloat("Simulation FPS", ref MemoryUtil.GetRef(sMain!.Instance + 0x58)); if (ImGui.BeginItemTooltip()) { ImGui.Text("This does not apply to jiggle physics."); ImGui.EndTooltip(); } ImGui.InputFloat("Max FPS", ref MemoryUtil.GetRef(sMain!.Instance + 0x5C)); ImGui.PopItemWidth(); ImGui.Text($"sLightLinker: {sLightLinker!.Instance:X}"); ImGui.Text($"sMhSpeedTree: {sMhSpeedTree!.Instance:X}"); } } #if SHADER_FEATURES private static bool replaceShaderFromFile(ShaderInfo *info, string path) { if (!System.IO.File.Exists(path)) { Log.Warn($"File not found: {path}."); return false; } byte[] shaderData; try { shaderData = System.IO.File.ReadAllBytes(path); } catch (Exception) { Log.Error($"Failed to read: {path}."); return false; } info->Replacement.Length = shaderData.Length; info->Replacement.Source = (byte *)Marshal.UnsafeAddrOfPinnedArrayElement(shaderData, 0); return true; } private string shaderPath = "nativePC/plugins/CSharp/Shaders"; private bool shadersLoadedOnce = false; public unsafe void OnCreateShader(ShaderInfo *info) { string hash = new string(info->DxbcHash); if (!shadersLoadedOnce && hash == "2e4e18fd-6ea5eeae-2de3d65c-26ab36dd") { if (replaceShaderFromFile(info, $"{shaderPath}/volume_upsample1.shdr")) { info->Replacement.Type = ShaderSourceType.BINARY; Log.Info("Volume upsample shader 1 replaced."); } } else if (!shadersLoadedOnce && hash == "e2c8c13e-45bc99b2-4c3d7699-3d66188e") { if (replaceShaderFromFile(info, $"{shaderPath}/volume_upsample2.shdr")) { info->Replacement.Type = ShaderSourceType.BINARY; Log.Info("Volume upsample shader 2 replaced."); } // This allows us to A/B using the volume rendering value 2 switch. //shadersLoadedOnce = true; } else if (hash == "8cf2a107-ecde214b-9939b350-7b2af1a7") { if (replaceShaderFromFile(info, $"{shaderPath}/cube_only.shdr")) { info->Replacement.Type = ShaderSourceType.BINARY; Log.Info("Cube Only shader replaced."); } } else if (hash == "b21dd223-98ab56ae-918264e6-eb6e8ee4") { if (replaceShaderFromFile(info, $"{shaderPath}/sslr.shdr")) { info->Replacement.Type = ShaderSourceType.BINARY; Log.Info("SSLR shader replaced."); } } else if (hash == "9ff245fb-4fd555e0-04d0ef15-84609fe1") { if (replaceShaderFromFile(info, $"{shaderPath}/fxaa_max_quality.hlsl")) { info->Replacement.Type = ShaderSourceType.HLSL; Log.Info("FXAA shader replaced."); } } else if (hash == "2eed1f00-e24d1b19-9d521064-17b9a586") { if (replaceShaderFromFile(info, $"{shaderPath}/body_skin.shdr")) { info->Replacement.Type = ShaderSourceType.BINARY; Log.Info("Body skin shader replaced."); } } else if (hash == "8e5e3c09-ebe77ee4-ba1cb659-d25bc7bf") { if (replaceShaderFromFile(info, $"{shaderPath}/face.shdr")) { info->Replacement.Type = ShaderSourceType.BINARY; Log.Info("Face shader replaced."); } } /* else if (hash == "18a797dc-9458d333-59e6dff0-9790fcea") { if (replaceShaderFromFile(info, $"{shaderPath}/sssss.shdr")) { info->Replacement.Type = ShaderSourceType.BINARY; Log.Info("SSSSS shader replaced."); } } else if (hash == "d7e47ffe-82572c64-795c4698-341e5b91") { if (replaceShaderFromFile(info, $"{shaderPath}/sslr_mips.shdr")) { info->Replacement.Type = ShaderSourceType.BINARY; Log.Info("SSLR mips shader replaced."); } } */ } #endif } }