//#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: // - Hair clipping while looking down. // - Optimized update logic. // - Only update non-override values when ui is shown. // - Map sky params. // - Lights refactor. // - Descriptions. // - float format. // - Min/Max. // - Lights iterop. // - SPL Patches: // - No crash on empty config. // - Style change // - Create light hook. // - 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 overlapped 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)); } private static string Vector3ToString(Vector3 v) { return $"{v.X},{v.Y},{v.Z}"; } private static Vector3 Vector3FromString(string s) { if (String.IsNullOrEmpty(s)) { throw new FormatException(); } string[] vs = s.Split(',').Select(v => v.Trim()).ToArray(); if (vs.Length != 3) { throw new FormatException(); } return new Vector3( Convert.ToSingle(vs[0]), Convert.ToSingle(vs[1]), Convert.ToSingle(vs[2])); } public enum ParameterType { BOOL, FLAG, BYTE, INT, HEX, COLOR, FLOAT, VECTOR3, VECTOR4, PATCH_FLOAT, SHADOW_RESOLUTION } static IPlugin? Instance = null; static Config getConfig() { return ConfigManager.GetConfig(Instance!); } static void saveConfig() { ConfigManager.SaveConfig(Instance!); } 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 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 = globalOverrides != null && selectedStage != currentStage; 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; public bool NoOverride; private nint offset; private int mask; 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; private Vector4 valueV = default; private int valueInt = 0; private Vector4 oValueV; // Original value. private int oValueInt; private bool pendingUpdate = true; private bool queuedOverride = false; private bool queuedValue = false; private Vector4 qValueV; // Queued value. private int qValueInt; private bool bValue = false; private Vector4 bValueV; // B value in A/B. private int bValueInt; public Parameter(string name, nint offset, int mask, ParameterType type, bool perFrame, bool noOverride) { Name = name; hiddenName = "##" + Name; this.offset = offset; this.mask = mask; Type = type; if (Type == ParameterType.PATCH_FLOAT) { valueV.X = MemoryUtil.Read(offset); pendingUpdate = false; } else if (Type == ParameterType.SHADOW_RESOLUTION) { valueInt = 1; pendingUpdate = false; } PerFrame = perFrame; NoOverride = noOverride; } public bool PendingUpdate() { return pendingUpdate; } public (Vector4, int) GetValue() { return (valueV, valueInt); } public int GetMask() { return mask; } private void maybeWriteCurrentValue() { switch (Type) { case ParameterType.PATCH_FLOAT: new Patch(offset, BitConverter.GetBytes(valueV.X)).Enable(); break; case ParameterType.SHADOW_RESOLUTION: if (valueInt > 1) { shadowResEnable(valueInt); } else { shadowResDisable(); } break; } if (PerFrame || lastAddr == 0x0) { return; } switch (Type) { case ParameterType.BOOL: 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; } } 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; maybeWriteCurrentValue(); 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; maybeWriteCurrentValue(); overrideValue = false; } } overrideForStage = false; } public void OverrideValue(Vector4 v4, int i1) { if (queuedOverride) { qValueV = v4; qValueInt = i1; queuedValue = true; } else { valueV = v4; valueInt = i1; maybeWriteCurrentValue(); } } 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 is updated // to the same value as a current override. switch (Type) { case ParameterType.BOOL: case ParameterType.BYTE: { ref byte b1 = ref MemoryUtil.GetRef(baseObject + offset); if (b1 != (byte)valueInt) { oValueInt = (byte)b1; } if (overrideValue) { b1 = (byte)valueInt; } else { valueInt = b1; } break; } case ParameterType.FLAG: { ref int i1 = ref MemoryUtil.GetRef(baseObject + offset); int b1 = ByteFlag((i1 & mask) == mask); if (b1 != valueInt) { oValueInt = b1; } if (overrideValue) { if (valueInt != 0) { i1 |= mask; } else { i1 &= ~mask; } } else { valueInt = b1; } break; } case ParameterType.INT: case ParameterType.HEX: case ParameterType.COLOR: { ref int i1 = ref MemoryUtil.GetRef(baseObject + offset); if (i1 != valueInt) { 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) { 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) { oValueV = new Vector4(v3.X, v3.Y, v3.Z, 0.0f); } if (overrideValue) { v3 = valueV3; } else { valueV = new Vector4(v3.X, v3.Y, v3.Z, 0.0f); } break; } case ParameterType.VECTOR4: { ref Vector4 v4 = ref MemoryUtil.GetRef(baseObject + offset); if (v4 != valueV) { 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; maybeWriteCurrentValue(); 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; float displayMinf = ignoreMinMax ? 0.0f : minf; float displayMaxf = ignoreMinMax ? 0.0f : maxf; switch (Type) { case ParameterType.BOOL: case ParameterType.FLAG: { bool b = i1 != 0; 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)) { valueChanged = true; } break; } case ParameterType.INT: case ParameterType.HEX: case ParameterType.SHADOW_RESOLUTION: { ImGui.SetNextItemWidth(width * defaultParamWidth); ImGuiInputTextFlags flags = ImGuiInputTextFlags.EnterReturnsTrue; if (Type == ParameterType.HEX) { flags |= ImGuiInputTextFlags.CharsHexadecimal; } if (ImGui.InputInt(label, ref i1, step, 0, flags)) { 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: case ParameterType.PATCH_FLOAT: { ImGui.SetNextItemWidth(width * defaultParamWidth); float f1 = v4.X; if (ImGui.DragFloat(label, ref f1, stepf, displayMinf, displayMaxf, "%.6f", ImGuiSliderFlags.NoRoundToFormat)) { v4 = new Vector4(f1, 0.0f, 0.0f, 0.0f); 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, displayMinf, displayMaxf, "%.5f", ImGuiSliderFlags.NoRoundToFormat)) { v4 = new Vector4(v3.X, v3.Y, v3.Z, 0.0f); valueChanged = true; } break; } case ParameterType.VECTOR4: { ImGui.SetNextItemWidth(width * defaultParamWidth * 4.0f); if (ImGui.DragFloat4(label, ref v4, stepf, displayMinf, displayMaxf, "%.5f", ImGuiSliderFlags.NoRoundToFormat)) { valueChanged = true; } break; } } if (valueChanged) { if (Type == ParameterType.SHADOW_RESOLUTION) { i1 = Math.Clamp(i1, 1, 5); // Hard limit. } else if (!ignoreMinMax) { switch (Type) { case ParameterType.BOOL: case ParameterType.FLAG: case ParameterType.BYTE: case ParameterType.INT: case ParameterType.HEX: i1 = Math.Clamp(i1, min, max); break; case ParameterType.FLOAT: case ParameterType.PATCH_FLOAT: case ParameterType.VECTOR3: case ParameterType.VECTOR4: v4 = new Vector4( Math.Clamp(v4.X, minf, maxf), Math.Clamp(v4.Y, minf, maxf), Math.Clamp(v4.Z, minf, maxf), Math.Clamp(v4.W, minf, maxf)); break; } } } return valueChanged; } public void DrawReferenceValue(string name, Vector4 v4, int i1) { ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.7f); switch (Type) { case ParameterType.BOOL: case ParameterType.FLAG: ImGui.Text($"({name}: {i1 != 0})"); break; case ParameterType.BYTE: case ParameterType.INT: case ParameterType.SHADOW_RESOLUTION: ImGui.Text($"({name}: {i1})"); break; case ParameterType.HEX: case ParameterType.COLOR: ImGui.Text($"({name}: {i1:X})"); break; case ParameterType.FLOAT: case ParameterType.PATCH_FLOAT: ImGui.Text($"({name}: {v4.X:0.000000})"); break; case ParameterType.VECTOR3: ImGui.Text($"({name}: {v4.X:0.00000}, {v4.Y:0.00000}, {v4.Z:0.00000})"); break; case ParameterType.VECTOR4: ImGui.Text($"({name}: {v4.X:0.00000}, {v4.Y:0.00000}, {v4.Z:0.00000}, {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 (NoOverride) { ImGui.SetCursorPos(ImGui.GetCursorPos() + new Vector2(40.0f, 0.0f)); } else 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)) { maybeWriteCurrentValue(); } 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); maybeWriteCurrentValue(); } 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))) { if (globalOverrides != null) { unsetIfOverridesContainsParam(globalOverrides, 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 { public Parameter(string name, nint offset, int mask, ParameterType type, T? min, T? max, T? step, bool perFrame, bool noOverride) : base(name, offset, mask, type, perFrame, noOverride) { if (typeof(T) == typeof(float)) { this.stepf = (step != null) ? Convert.ToSingle(step) : 0.1f; 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) || typeof(T) == typeof(bool)) { this.step = (step != null) ? Convert.ToInt32(step) : 0; 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; } else if (typeof(T) == typeof(bool)) { this.min = 0; this.max = 1; } } } } public static (float?, float?, float?) pStep(float step) { return (null, null, step); } public static (float?, float?, float?) pMin(float min) { return (min, null, null); } public static (float?, float?, float?) pMax(float max) { return (null, max, null); } public static (float?, float?, float?) pMinStep(float min, float step) { return (min, null, step); } public static (float?, float?, float?) pMaxStep(float max, float step) { return (null, max, step); } public static (float?, float?, float?) pMinMaxStep(float min, float max, float step) { return (min, max, step); } public static (int?, int?, int?) pStep(int step) { return (null, null, step); } public static (int?, int?, int?) pMin(int min) { return (min, null, null); } public static (int?, int?, int?) pMax(int max) { return (null, max, null); } public static (int?, int?, int?) pMinStep(int min, int step) { return (min, null, step); } public static (int?, int?, int?) pMaxStep(int max, int step) { return (null, max, step); } public static (int?, int?, int?) pMinMaxStep(int min, int max, int step) { return (min, max, step); } public static (byte?, byte?, byte?) pStep(byte step) { return (null, null, step); } public static Parameter newParameter(string name, nint offset, ParameterType type, (T? min, T? max, T? step) m = default) where T : unmanaged { return new Parameter(name, offset, 0, type, m.min, m.max, m.step, false, false); } public static Parameter newFrameParameter(string name, nint offset, ParameterType type, (T? min, T? max, T? step) m = default) where T : unmanaged { return new Parameter(name, offset, 0, type, m.min, m.max, m.step, true, false); } public static Parameter newParameterView(string name, nint offset, ParameterType type, (T? min, T? max, T? step) m = default) where T : unmanaged { return new Parameter(name, offset, 0, type, m.min, m.max, m.step, false, true); } public static Parameter newFlag(string name, nint offset, ParameterType type, int mask) where T : unmanaged { return new Parameter(name, offset, mask, type, null, null, null, false, false); } 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 => (Param != null) ? Param.Name : ""; set => Param = getParameterByName(value); } public string Value { get { if (Param != null) { switch (Param.Type) { case ParameterType.BOOL: case ParameterType.FLAG: return (sValueInt != 0).ToString(); case ParameterType.BYTE: return sValueInt.ToString(); case ParameterType.INT: case ParameterType.SHADOW_RESOLUTION: return sValueInt.ToString(); case ParameterType.HEX: case ParameterType.COLOR: return sValueInt.ToString("X8"); case ParameterType.FLOAT: case ParameterType.PATCH_FLOAT: return sValueV.X.ToString(); case ParameterType.VECTOR3: Vector3 sValueV3 = new Vector3(sValueV.X, sValueV.Y, sValueV.Z); return Vector3ToString(sValueV3); case ParameterType.VECTOR4: break; } } return ""; } set { if (Param == null) return; try { switch (Param.Type) { case ParameterType.BOOL: case ParameterType.FLAG: valueInt = ByteFlag(Convert.ToBoolean(value)); break; case ParameterType.BYTE: valueInt = Convert.ToInt32(value); break; case ParameterType.INT: case ParameterType.SHADOW_RESOLUTION: 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: Vector3 valueV3 = Vector3FromString(value); valueV = new Vector4(valueV3.X, valueV3.Y, valueV3.Z, 0.0f); break; case ParameterType.VECTOR4: break; } } catch (FormatException) { Log.Warn($"Failed to parse value of '{Name}' override."); } catch (OverflowException) { Log.Warn($"Value of '{Name}' override too large."); } Save(); } } // This could different from the Parameter's valueV/valueInt if this override is unset. private Vector4 valueV = default; private int valueInt = 0; private bool isSet = false; private Vector4 sValueV = default; // Saved value. 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, bool preset, float width) { bool fade = superseded; if (fade) { Assert(!isSet); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (currentOverrides == null) { 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 (iterParam.NoOverride) continue; 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); } } } } Param = iterParam; (valueV, valueInt) = Param.GetValue(); Save(); if (preset) { superseded = overridesContainsParam(externalOverrides.Values, Param); if (!superseded && wasSet) { if (!(stageOverrides != null && unsetIfOverridesContainsParam(stageOverrides, Param))) { if (globalOverrides != null) { unsetIfOverridesContainsParam(globalOverrides, Param); } } Set(); } } else { Config config = getConfig(); superseded = selectedOverrideSuperseded(config, globalOverrides, stageOverrides, Param); if (!superseded && wasSet) { if (globalOverrides != null) { unsetIfOverridesContainsParam(globalOverrides, Param); } Set(); } } } if (isSelected) ImGui.SetItemDefaultFocus(); } } ImGui.EndCombo(); } ImGui.PopItemWidth(); if (currentOverrides == null) { ImGui.PopItemFlag(); } if (Param != null) { ImGui.SameLine(); if (Param.DrawValue(ref valueV, ref valueInt, width, false)) { if (isSet) { Param.OverrideValue(valueV, valueInt); } } (Vector4 v4, int i1) = Param.GetValue(); bool valueDiffers = (v4, i1) != (valueV, valueInt); // @TODO: Not sValue and valueDiffers should be seperated/more clear. 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 (fade) { 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 (fade) { ImGui.PopStyleVar(); } } } // Start of Parameter Definitions. private delegate void OnAreaChange(nint unknownPtr); private Hook? onAreaChange; private static MtObject? sMhScene = null; private static Parameter hqMode = newParameter("HQ Mode", 0xE9A3, ParameterType.BOOL); private static Parameter[] lodParameters = { newParameter("LOD Bias 1", 0x21C, ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Bias 2", 0x220, ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Caster Bias", 0x1E4, ParameterType.FLOAT, pMin(0.0f)), // If platform is PS4/Xbox/PC it appears [2] is PC. newParameter("LOD Length Platform Bias[2]", 0x22C + (4 * 2), ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Pixel Size Platform Bias[2]", 0x244 + (4 * 2), ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Limit", 0xE898, ParameterType.INT, pStep(1)), newParameter("LOD Passthrough Culling Rate", 0x1F0, ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Far Culling Fade Length Max", 0x20C, ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Far Culling Fade Pixel Max", 0x210, ParameterType.FLOAT, pMin(0.0f)), newParameter("Speed Tree LOD Billboard Fade Range", 0x214, ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Culling Length Bias", 0x224, ParameterType.FLOAT, pMin(0.0f)), newParameter("LOD Culling Pixel Bias", 0x228, ParameterType.FLOAT, pMin(0.0f)) }; private static Parameter[] snowParameters = { newParameter("Snow Field 4 Global LOD Param", 0x5718, ParameterType.FLOAT) }; private static Parameter[] passthroughParameters = { newParameter("Passthrough Active", 0xE9A0, ParameterType.BOOL), newParameter("Passthrough Culling Active", 0xE9A2, ParameterType.BOOL), newParameterView("Passthrough Near", 0xE940, ParameterType.FLOAT), newParameterView("Passthrough Far", 0xE944, ParameterType.FLOAT), newParameter("Passthrough Near Alpha", 0xE948, ParameterType.FLOAT), newParameter("Passthrough Far Alpha", 0xE94C, ParameterType.FLOAT), newParameter("Passthrough Correct", 0xE950, ParameterType.FLOAT) }; private static Parameter[] shadowCascadeParameters = { newParameter("Primary Shadow HQ", 0xE5C0, ParameterType.BOOL), newParameter("Primary Shadow HQ (HQ)", 0xE5C2, ParameterType.BOOL), newParameter("Shadow Cascade 2Way Bias (HQ)", 0x58, ParameterType.FLOAT), newParameter("Primary Shadow Sample Num (HQ)", 0xE5CC, ParameterType.INT, pStep(1)) }; private delegate void EvalSceneParams(nint unknownPtr, nint unknownPtr2, int unknownInt, nint unknownPtr3); private Hook? evalSceneParams; private static Parameter[] broadAreaShadowParameters = { newParameter("Broad Area Shadow Enable", 0x5534, ParameterType.BOOL), newParameter("Broad Area Shadow Center", 0x5540 + 0x40, ParameterType.VECTOR3), newParameter("Broad Area Shadow Range", 0x5560 + 0x40, ParameterType.VECTOR3), newParameter("Broad Area Shadow Direction", 0x5550 + 0x40, ParameterType.VECTOR3, pStep(0.00025f)), newParameter("Broad Area Shadow Max LOD Level", 0x5524, ParameterType.FLOAT), newParameter("Broad Area Shadow Culling Size", 0x5528, ParameterType.FLOAT), newParameter("Broad Area Shadow Precision", 0x5518, ParameterType.INT, pMinMaxStep(0, 2, 1)) }; private static Parameter[] contactShadowParameters = { newParameter("Fake Light Intensity", 0xEC40, ParameterType.FLOAT), // MonsterHunterWorld.exe+1B18711 - mov [rbx+0000EC44],3F800000 (Other unknown reference). newParameter("Fake Light Intensity (Gameplay)", new IntPtr(0x141B1A19F) + 0x6, ParameterType.PATCH_FLOAT), newParameter("Fake Light Blend", new IntPtr(0x141CDE0CE) + 0x6, ParameterType.PATCH_FLOAT), newParameter("Force Disable Contact Shadows", 0xB4ED, ParameterType.BOOL), newParameter("Facial Contact Shadows Enabled", 0xB4EE, ParameterType.BOOL), newParameter("Facial Contact Shadows Enable Noise", 0xB501, ParameterType.BOOL), newParameter("Contact Shadow Accept Maximum Length", 0xB4F8, ParameterType.FLOAT), newParameter("Contact Shadow Accept Minimum Length", 0xB4FC, ParameterType.FLOAT) }; private delegate void UpdateCapsuleAOParams(nint sceneObjectInternal); private Hook? updateCapsuleAoParams; private static Parameter[] capsuleLightParameters = { newParameter("Force Disable Capsule AO", 0xB503, ParameterType.BOOL), newParameter("Capsule AO Enabled", 0xB502, ParameterType.BOOL), newParameter("Capsule AO Distance Fall Coef", 0xE5B4, ParameterType.FLOAT, pStep(0.0001f)), newParameter("Capsule AO Light Channel Mask", 0xE5BC, ParameterType.INT, pStep(1)), newParameter("Capsule Light Dir XZY", 0xE548, ParameterType.VECTOR3), newParameterView("Capsule Light W Dir", 0xE560, ParameterType.VECTOR3), newParameter("Capsule Light Angle", 0xE5B0, ParameterType.FLOAT), newParameter("Capsule AO Intensity", 0xE5B8, ParameterType.FLOAT) }; private delegate void UpdateSSAOParams(nint stackOffset); private Hook? updateSSAOParams; private static Parameter[] ssaoParameters = { newParameter("SSAO Depth Bias", 0xB4B0, ParameterType.FLOAT, pStep(0.00001f)), newParameter("SSAO Sloped Depth Bias", 0xB4B4, ParameterType.FLOAT, pStep(0.00001f)), newParameter("SSAO Max Depth Bias", 0xB4B8, ParameterType.FLOAT, pStep(0.00001f)), newParameter("SSAO Dispersion", 0xB4BC, ParameterType.FLOAT), newParameter("SSAO Effect", 0xB430, ParameterType.FLOAT), newParameter("SSAO Effect GI", 0xB434, ParameterType.FLOAT), newParameter("SSAO Depth Difference", 0xB4C0, ParameterType.FLOAT), newParameter("SSAO Samples Per Pixel", 0xB4C4, ParameterType.FLOAT), newParameter("SSAO Max Sample Num", 0xB4C8, ParameterType.INT, pStep(1)), newParameter("SSAO Max Sample Num (HQ)", 0xB4D0, ParameterType.INT, pStep(1)), newParameter("SSAO Radius", 0xB4D4, ParameterType.FLOAT), newParameter("SSAO Bias", 0xB4D8, ParameterType.FLOAT), newParameter("SSAO Intensity", 0xB4E0, ParameterType.FLOAT), newParameter("SSAO Use HiZ", 0xB4E5, ParameterType.BOOL), newParameter("SSAO Edge Atten Rate", 0xB4DC, ParameterType.FLOAT) }; private delegate void UpdateSSLRParams(nint stackOffset); private Hook? updateSSLRParams; private static Parameter[] sslrParameters = { newParameter("SSLR Loop Count", 0xE628, ParameterType.INT), newParameter("SSLR Loop Count Factor for CBR", 0xE62C, ParameterType.FLOAT), newParameter("SSLR Eliminate Depth", 0xE630, ParameterType.FLOAT), newParameter("SSLR Accurate Threshold", 0xE644, ParameterType.FLOAT), newParameter("SSLR Accurate Threshold (HQ)", 0xE64C, ParameterType.FLOAT), newParameter("SSLR Dither Radius", 0xE634, ParameterType.FLOAT), newParameter("SSLR Importance Bias", 0xE638, ParameterType.FLOAT), newParameter("SSLR Mip Scale", 0xE63C, ParameterType.FLOAT), newParameter("SSLR Mip Bias", 0xE640, ParameterType.FLOAT), newParameter("SSLR Dither Resolve", 0xB43F, ParameterType.BOOL), newParameter("SSLR Edge Atten Rate", 0xB428, ParameterType.FLOAT), newParameter("SSLR Mip 0 Count Threshold", 0xB438, ParameterType.INT, pStep(1)), newParameter("SSLR Depth Eliminate Rate", 0xB42C, ParameterType.FLOAT), newParameter("SSLR Use Mipmap", 0xE650, ParameterType.BOOL), newParameter("SSLR GBuffer Jitter", 0xB440, ParameterType.BOOL), newParameter("SSLR Intensity", 0xB424, ParameterType.FLOAT) }; private static Parameter shadowResParameter = newParameter("Shadow Resolution Multiplier", 0, ParameterType.SHADOW_RESOLUTION, pStep(1)); private delegate void UpdateShadowParams(nint shadowParams, nint stackOffset); private delegate void StaticShadowParams(nint shadowParamsStatic, nint shadowObjectInternal); private Hook? updateShadowParams; private Hook? staticShadowParams; private static Parameter[] shadowParameters1 = { }; // This is actually an InfiniteLight object. // Non-PerFrame parameters will be written directly to the light object. private static Parameter[] shadowParameters2 = { newFrameParameter("Sunlight Group", 0x14, ParameterType.INT, pMinMaxStep(1, 255, 1)), newFrameParameter("Sunlight Direction", 0x110, ParameterType.VECTOR3, pStep(0.00025f)), newFrameParameter("Sunlight Color", 0xE0, ParameterType.FLOAT, pStep(0.001f)), newFrameParameter("Sunlight Intensity", 0xF4, ParameterType.FLOAT), newParameter("Sunlight Min Roughness", 0x58C, ParameterType.FLOAT, pStep(0.0025f)), // +0xFC in params but is never read. newFrameParameter("Sunlight Is Primary", 0x1D, ParameterType.BOOL), newFrameParameter("Do Volumetric", 0x1F, ParameterType.BOOL), newFrameParameter("Shadow Cast", 0x23, ParameterType.BOOL), newFrameParameter("Shadow Map Size", 0x30, ParameterType.INT), // Aligned to 128. newFrameParameter("Shadow Near Clip Distance", 0x28, ParameterType.FLOAT), newFrameParameter("Shadow Cascade Mode", 0x174, ParameterType.INT, pMinMaxStep(0, 3, 1)), newFrameParameter("Shadow Cascade 2Way Bias", 0x17C, ParameterType.FLOAT), newFrameParameter("Shadow Distance", 0x6C, ParameterType.FLOAT, pStep(5.0f)), newFrameParameter("Shadow Backforward Distance", 0x7C, ParameterType.FLOAT, pStep(5.0f)), newFrameParameter("Shadow Is Fixed Fov Mode", 0x1F9, ParameterType.BOOL), newFrameParameter("Shadow Fov", 0x1F4, ParameterType.FLOAT, pStep(0.0001f)), newFrameParameter("Shadow Is Discretization Fov Mode", 0x205, ParameterType.BOOL), newFrameParameter("Shadow Discretization Angle", 0x200, ParameterType.FLOAT, pStep(0.0001f)), newFrameParameter("Shadow Distribution", 0x74, ParameterType.FLOAT, pStep(0.001f)), newFrameParameter("Cascade Manual Split", 0x4D, ParameterType.BOOL), newFrameParameter("Cascade Manual Split Distance[0]", 0x54, ParameterType.FLOAT), newFrameParameter("Cascade Manual Split Distance[1]", 0x5C, ParameterType.FLOAT), newFrameParameter("Cascade Manual Split Distance[2]", 0x64, ParameterType.FLOAT), newFrameParameter("Shadow Sloped Depth Bias", 0x40, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Shadow Depth Bias", 0x38, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Shadow Max Depth Bias", 0x48, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Individual Shadow Bias", 0x81, ParameterType.BOOL), newFrameParameter("Cascade Depth Bias[0]", 0x88, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Sloped Depth Bias[0]", 0xA0, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Max Depth Bias[0]", 0xB8, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Depth Bias[1]", 0x90, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Sloped Depth Bias[1]", 0xA8, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Max Depth Bias[1]", 0xC0, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Depth Bias[2]", 0x98, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Sloped Depth Bias[2]", 0xB0, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Cascade Max Depth Bias[2]", 0xC8, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Broad Area Shadow Depth Bias", 0x1C4, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Broad Area Shadow Sloped Depth Bias", 0x1CC, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Broad Area Shadow Max Depth Bias", 0x1D4, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Projection Scale", 0x134, ParameterType.VECTOR3), newFrameParameter("Projection Offset Speed", 0x144, ParameterType.FLOAT), newFrameParameter("Projection Up", 0x160, ParameterType.VECTOR3), newFrameParameter("Primary Shadow Sample Num", 0x1DC, ParameterType.INT, pStep(1)), newFrameParameter("Primary Shadow Radius", 0x1EC, ParameterType.FLOAT, pStep(0.0001f)) }; private static MtObject? sLightProbes = null; private static Parameter[] lightProbesParameters = { newParameter("Debug Mode", 0x98, ParameterType.INT, pStep(1)) }; private delegate nint CreateLightingObject(); private delegate nint DestroyLightingObject(nint lightingObjectInternal, int unknownInt); private Hook? createLightingObject; private Hook? destroyLightingObject; private nint lightingObject = 0x0; private delegate void UpdateLightingParameters(nint stackOffset, nint lightingObjectInternal); private Hook? updateLightingParams; private static Parameter[] lightingParameters = { newParameter("Light Tone Map Type", 0x16C, ParameterType.INT, pMinMaxStep(1, 6, 1)), newParameter("Light Shoulder Strength", 0x168, ParameterType.BYTE, pStep((byte)1)), newParameter("Light Linear Strength", 0x178, ParameterType.FLOAT, pStep(0.001f)), newParameter("Light Linear Angle", 0x17C, ParameterType.FLOAT, pStep(0.001f)), newParameter("Light Toe Strength", 0x180, ParameterType.FLOAT, pStep(0.001f)), newParameter("Light Toe Num", 0x184, ParameterType.FLOAT, pStep(0.001f)), newParameter("Light White Point", 0x18C, ParameterType.FLOAT, pStep(0.01f)), newParameter("Light Enable Color Grading", 0x1D8, ParameterType.BOOL), newParameter("Volume Dispersion", 0x19C, ParameterType.FLOAT), newParameter("Volume Edge Sharpness", 0x198, ParameterType.FLOAT, pStep(0.00001f)), newParameter("Volume Downsample", 0x210, ParameterType.BOOL) }; 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 delegate void UpdateBloomParams(nint bloomObjectInternal, nint unknownPtr); //private Hook? updateBloomParams; private nint bloomObject = 0x0; private static Parameter[] bloomParameters = { // Disabling 'Draw' is a bad way to disable bloom. It breaks the filter on initial load which // can be seen when returning to title or opening a guild card for the first time. Setting 'Bloom Renormalize' // to 0.0 or 'Bloom Reduction Resolution' to -1 can both effectively disable bloom without that issue. //newFlag("Bloom Enabled", 0x14, ParameterType.FLAG, (1 << 1)), // Draw newParameter("Bloom Threshold", 0x204, ParameterType.FLOAT), newParameter("Bloom Renormalize", 0x208, ParameterType.FLOAT, pMin(0.0f)), newParameter("Bloom Color", 0x200, ParameterType.COLOR), newParameter("Bloom Downsample Count", 0x168, ParameterType.INT, pMinMaxStep(0, 8, 1)), newParameter("Bloom Reduction Resolution", 0x16C, ParameterType.INT, pStep(1)), newParameter("Bloom Is SRGB Gamut", 0x214, ParameterType.BOOL), newParameter("Bloom Compute Luminance", 0x241, ParameterType.BOOL) }; 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 = { newParameter("Dof Enabled", 0x1DD, ParameterType.BOOL), newParameter("Dof New Version", 0x1DC, ParameterType.BOOL), newFrameParameter("Dof F Number", 0x1A0, ParameterType.FLOAT, pMinStep(0.0f, 0.001f)), newFrameParameter("Dof Sensor Size", 0x1A4, ParameterType.FLOAT, pStep(0.001f)), newFrameParameter("Dof Focus Distance", 0x1A8, ParameterType.FLOAT, pStep(0.2f)), newParameter("Dof Near Coef", 0x1CC, ParameterType.FLOAT, pStep(0.001f)), newParameter("Dof Far Coef", 0x1C8, ParameterType.FLOAT, pStep(0.001f)), newParameter("Dof Near Enable", 0x1DE, ParameterType.BOOL), newParameter("Dof Far Enable", 0x1DF, ParameterType.BOOL), newParameter("Dof Debug Draw", 0x1E0, ParameterType.BOOL), newParameter("Dof Radius", 0x1D0, ParameterType.FLOAT, pStep(0.001f)), newParameter("Dof Depth Scale Foreground", 0x1D4, ParameterType.FLOAT, pMinStep(0.0f, 0.0001f)), newParameter("Dof Aspect", 0x1D8, ParameterType.FLOAT, pMinStep(0.0f, 0.001f)), newFrameParameter("Vignetting Enabled", 0x200, ParameterType.BOOL), newFrameParameter("Vignetting Ellipse", 0x201, ParameterType.BOOL), newFrameParameter("Vignetting Ellipticity", 0x204, ParameterType.FLOAT, pStep(0.01f)), newFrameParameter("Vignetting Offset", 0x1F8, ParameterType.FLOAT, pStep(0.001f)), newFrameParameter("Vignetting Pow", 0x1FC, ParameterType.FLOAT, pStep(0.01f)), newFrameParameter("Vignetting Color", 0x208, ParameterType.COLOR) }; 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 = { newParameter("Motion Blur Type", 0x168, ParameterType.INT, pStep(1)), newParameter("Motion Blur Sample Num", 0x180, ParameterType.INT, pMinStep(0, 1)), newFrameParameter("Motion Blur Shutter Speed", 0x184, ParameterType.FLOAT, pMinStep(0.0f, 0.001f)), newParameter("Motion Blur Fur Shutter Speed", 0x188, ParameterType.FLOAT, pStep(0.001f)), newParameter("Motion Blur Threshold", 0x18C, ParameterType.FLOAT, pMinStep(0.0f, 0.01f)) }; 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 = { newParameter("FXAA Subpix", 0x178, ParameterType.FLOAT, pMinMaxStep(0.0f, 1.0f, 0.005f)), newParameter("FXAA Edge Threshold", 0x17C, ParameterType.FLOAT, pMinMaxStep(0.0f, 1.0f, 0.001f)), newParameter("FXAA Edge Threshold Min", 0x180, ParameterType.FLOAT, pMinMaxStep(0.0f, 1.0f, 0.001f)) }; private static MtObject? sMain = null; private static Parameter[] allParameters = new Parameter[]{ hqMode } .Concat(lodParameters) .Concat(snowParameters) .Concat(new Parameter[] { shadowResParameter }) .Concat(shadowParameters2) .Concat(shadowCascadeParameters) .Concat(broadAreaShadowParameters) .Concat(contactShadowParameters) .Concat(capsuleLightParameters) .Concat(ssaoParameters) .Concat(sslrParameters) .Concat(passthroughParameters) .Concat(lightingParameters) .Concat(bloomParameters) .Concat(dofParameters) .Concat(motionBlurParameters) .Concat(fxaaParameters).ToArray(); private static int currentShadowResMultiplier = 1; private static NativeAction setShadowQuality; private static Patch shadowRes1_2x; private static Patch shadowRes1_3x; private static Patch shadowRes1_4x; private static Patch shadowRes1_5x; private static Patch shadowRes1_1; private static Patch shadowRes1_2; private static Patch shadowRes1_3; private static Patch shadowRes1_4; private static Patch shadowRes2; private static Patch shadowRes3; private static void shadowResEnable(int multiplier) { if (multiplier == currentShadowResMultiplier) { return; } Assert(multiplier != 1); switch (currentShadowResMultiplier) { case 1: shadowRes1_1.Enable(); shadowRes1_2.Enable(); shadowRes1_3.Enable(); shadowRes1_4.Enable(); shadowRes2.Enable(); shadowRes3.Enable(); break; case 2: shadowRes1_2x.Disable(); break; case 3: shadowRes1_3x.Disable(); break; case 4: shadowRes1_4x.Disable(); break; case 5: shadowRes1_5x.Disable(); break; } switch (multiplier) { case 2: shadowRes1_2x.Enable(); break; case 3: shadowRes1_3x.Enable(); break; case 4: shadowRes1_4x.Enable(); break; case 5: case 6: shadowRes1_5x.Enable(); break; } currentShadowResMultiplier = multiplier; setShadowQuality.Invoke(MemoryUtil.Read(sMhScene!.Instance + 0x5530) + 1); } private static void shadowResDisable() { if (currentShadowResMultiplier == 1) { return; } switch (currentShadowResMultiplier) { case 2: shadowRes1_2x.Disable(); break; case 3: shadowRes1_3x.Disable(); break; case 4: shadowRes1_4x.Disable(); break; case 5: shadowRes1_5x.Disable(); break; } shadowRes1_1.Disable(); shadowRes1_2.Disable(); shadowRes1_3.Disable(); shadowRes1_4.Disable(); shadowRes2.Disable(); shadowRes3.Disable(); currentShadowResMultiplier = 1; setShadowQuality.Invoke(MemoryUtil.Read(sMhScene!.Instance + 0x5530) + 1); } 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 = { newParameter("Group", 0x13C, ParameterType.INT, pMinMaxStep(1, 255, 1)), // 1 = World, (1 << 1) = Player/Palico, (1 << 2) = NPCs newFrameParameter("Position", 0x5A0, ParameterType.VECTOR3, pStep(0.15f)), newFrameParameter("Color", 0x140, ParameterType.COLOR), newFrameParameter("Intensity", 0x8A8, ParameterType.FLOAT), newFrameParameter("Radius", 0x6A0, ParameterType.FLOAT, pStep(0.25f)), newFrameParameter("Effective Radius", 0x588, ParameterType.FLOAT, pStep(0.25f)), newFrameParameter("Min Roughness", 0x58C, ParameterType.FLOAT, pStep(0.0025f)), newParameter("Do Volumetric", 0x590, ParameterType.BOOL), newFlag("Shadow Cast", 0x138, ParameterType.FLAG, (1 << 23)), newParameter("Shadow Map Size", 0x1B8, ParameterType.INT), // Aligned to 128. newParameter("Shadow Near Clip Distance", 0x1B4, ParameterType.FLOAT), newFrameParameter("Shadow Depth Bias", 0x57C, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Shadow Sloped Depth Bias", 0x580, ParameterType.FLOAT, pStep(0.00001f)), newFrameParameter("Shadow Max Depth Bias", 0x584, ParameterType.FLOAT, pStep(0.00001f)), }; 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, 255); 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; // Assert no duplicate names. foreach (Parameter param in allParameters) { Assert(Array.FindAll(allParameters, (p => p.Name == param.Name)).Length == 1); } onAreaChange = Hook.Create(0x141AC27D0, OnAreaChangeHook); // nint // Needed for at least "Broad Area Shadow Dir". Otherwise it will flicker during an update on center change. evalSceneParams = Hook.Create(0x1423C3790, EvalSceneParamsHook); // nint, nint, int, nint // Light Dir XZY is updated seperately at 0x1420397F0, we assume this function always happens later, though. updateCapsuleAoParams = Hook.Create(0x141B19E10, UpdateCapsuleAOParamsHook); // nint // These run during cutscenes. updateSSAOParams = Hook.Create(0x1416D8B10, UpdateSSAOParamsHook); // nint updateSSLRParams = Hook.Create(0x1416DA3F0, UpdateSSLRParamsHook); // nint updateLightingParams = Hook.Create(0x1416DAAB0, UpdateLightingParametersHook); // nint, nint createLightingObject = Hook.Create(0x1424CDE20, CreateLightingObjectHook); destroyLightingObject = Hook.Create(0x1424CE380, DestroyLightingObjectHook); // nint, int createBloomObject = Hook.Create(0x1424CB3C0, CreateBloomObjectHook); createBloomObject2 = Hook.Create(0x1424CB5E0, CreateBloomObject2Hook); // nint destroyBloomObject = Hook.Create(0x1424CB750, DestroyBloomObjectHook); // nint, int //updateBloomParams = Hook.Create(0x1424CD570, UpdateBloomParamsHook); // nint, nint 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 createFXAAObject = Hook.Create(0x142393560, CreateFXAAObjectHook); createFXAAObject2 = Hook.Create(0x142393680, CreateFXAAObject2Hook); // nint destroyFXAAObject = Hook.Create(0x142393740, DestroyFXAAObjectHook); // 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); // @TODO: Allocate our own memory for this? Assert(MemoryUtil.Read(0x142EE1A90) == 2.0f); Assert(MemoryUtil.Read(0x142E4FE5C) == 3.0f); Assert(MemoryUtil.Read(0x142C4A2F0) == 4.0f); Assert(MemoryUtil.Read(0x142F0F260) == 5.0f); shadowRes1_2x = new Patch(addr + 0x8, [0x04, 0x1B, 0xAA, 0x02]); // 2.0. 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_5x = new Patch(addr + 0x8, [0xD4, 0xF2, 0xAC, 0x02]); // 5.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. shadowRes1_4 = new Patch(new nint(0x142288AAA) + 0x4, [0x9E]); // Limit 4.0 -> 5.0. // 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. @TODO: Figure this out. 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; 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")!; 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(); } saveConfig(); } /* 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) { hqMode.Update(sMhScene!.Instance); foreach (Parameter param in lodParameters) { param.Update(sMhScene!.Instance); } foreach (Parameter param in passthroughParameters) { param.Update(sMhScene!.Instance); } foreach (Parameter param in shadowCascadeParameters) { param.Update(sMhScene!.Instance); } foreach (Parameter param in contactShadowParameters) { param.Update(sMhScene!.Instance); } foreach (Parameter param in snowParameters) { param.Update(sMhScene!.Instance); } foreach (Parameter param in ssaoParameters) { param.Update(sMhScene!.Instance); } foreach (Parameter param in sslrParameters) { param.Update(sMhScene!.Instance); } if (lightingObject != 0x0) { foreach (Parameter param in lightingParameters) { param.Update(lightingObject); } } foreach (Parameter param in lightProbesParameters) { param.Update(sLightProbes!.Instance); } if (bloomObject != 0x0) { foreach (Parameter param in bloomParameters) { param.Update(bloomObject); } } 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 (fxaaObject != 0x0) { foreach (Parameter param in fxaaParameters) { param.Update(fxaaObject); } } } private void EvalSceneParamsHook(nint unknownPtr, nint unknownPtr2, int unknownInt, nint unknownPtr3) { foreach (Parameter param in broadAreaShadowParameters) { param.Update(sMhScene!.Instance); } evalSceneParams!.Original(unknownPtr, unknownPtr2, unknownInt, unknownPtr3); } private void UpdateCapsuleAOParamsHook(nint sceneObjectInternal) { updateCapsuleAoParams!.Original(sceneObjectInternal); foreach (Parameter param in capsuleLightParameters) { param.Update(sMhScene!.Instance); } } private void UpdateSSAOParamsHook(nint stackOffset) { updateSSAOParams!.Original(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 void UpdateLightingParametersHook(nint stackOffset, nint lightingObjectInternal) { updateLightingParams!.Original(stackOffset, lightingObjectInternal); if (lightingObject == lightingObjectInternal) { foreach (Parameter param in lightingParameters) { param.Update(lightingObject); } } } 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 void UpdateBloomParamsHook(nint bloomObjectInternal, nint unknownPtr) { if (bloomObject == bloomObjectInternal) { foreach (Parameter param in bloomParameters) { param.Update(bloomObject); } } updateBloomParams!.Original(bloomObjectInternal, unknownPtr); } */ 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 shadowParams, nint stackOffset) { bool isPrimary = MemoryUtil.Read(stackOffset + 0x1D) == 0x1; if (isPrimary) { foreach (Parameter param in shadowParameters2) { if (param.PerFrame) { param.Update(stackOffset); } } } updateShadowParams!.Original(shadowParams, stackOffset); } private void StaticShadowParamsHook(nint shadowParamsStatic, nint shadowObjectInternal) { if (shadowObjectInternal == 0x0) { // Necessary to apply Broad Area Depth Bias overrides on first update. foreach (Parameter param in shadowParameters2) { if (param.PerFrame) { param.Update(shadowParamsStatic); } } } else { foreach (Parameter param in shadowParameters2) { if (!param.PerFrame) { param.Update(shadowObjectInternal); } } } staticShadowParams!.Original(shadowParamsStatic, 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 (selectedStage == currentStage || 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, false, 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(); } saveConfig(); } 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, true, 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(); } saveConfig(); } } ImGui.PopID(); ImGui.Separator(); ImGui.PushID("External"); ImGui.Text("External:"); foreach (Override ovE in externalOverrides.Values) { ovE.Draw(null, null, null, false, false, width); } ImGui.PopID(); ImGui.Separator(); if (ImGui.CollapsingHeader("Patches")) { Config.PatchConfig patches = config.Patches; 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; saveConfig(); } 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; saveConfig(); } 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; saveConfig(); } 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; saveConfig(); } 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; saveConfig(); } 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(); } bool expanded = ImGui.CollapsingHeader("Parameters"); if (ImGui.BeginItemTooltip()) { ImGui.Text("(Original: X) may be inaccurate for various reasons, such as\n 1. The game set the parameter to the same value as an override from this mod.\n 2. The game is dynamically updating the parameter in a location unknown to this mod."); ImGui.EndTooltip(); } if (expanded) { hqMode.Draw(width); ImGui.PushID("LOD"); if (ImGui.CollapsingHeader("Level of Detail")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in lodParameters) { param.Draw(width); } foreach (Parameter param in snowParameters) { param.Draw(width); } } ImGui.PopID(); // MonsterHunterWorld.exe+1AB99E0 - mov rax,[MonsterHunterWorld.exe+500E180] nint shadowAddr = MemoryUtil.Read(0x14500E180); nint shadowObject = MemoryUtil.Read(shadowAddr + 0x4B0); shadowAddr += 0x5B0; ImGui.PushID("Shadows"); if (ImGui.CollapsingHeader("Sunlight and Shadows")) { shadowResParameter.Draw(width); ImGui.Text($"shadowObject: 0x{shadowAddr:X}, shadowAddr: 0x{shadowObject:X}"); /* if (ImGui.CollapsingHeader("Pass 1")) { 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.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in shadowCascadeParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("BroadAreaShadows"); if (ImGui.CollapsingHeader("Broad Area Shadows")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in broadAreaShadowParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("ContactLight"); if (ImGui.CollapsingHeader("Contact Light")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in contactShadowParameters) { param.Draw(width); } foreach (Parameter param in capsuleLightParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("SSAO"); if (ImGui.CollapsingHeader("Ambient Occlusion")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in ssaoParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("SSLR"); if (ImGui.CollapsingHeader("Reflections")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in sslrParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("Passthrough"); if (ImGui.CollapsingHeader("Passthrough")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in passthroughParameters) { param.Draw(width); } } ImGui.PopID(); /* ImGui.PushID("Snow"); if (ImGui.CollapsingHeader("Snow")) { ImGui.Text($"Address: 0x{sMhScene!.Instance:X}"); foreach (Parameter param in snowParameters) { param.Draw(width); } } ImGui.PopID(); */ ImGui.PushID("Lighting"); if (ImGui.CollapsingHeader("Lighting")) { if (lightingObject != 0x0) { ImGui.Text($"Address: 0x{lightingObject:X}"); foreach (Parameter param in lightingParameters) { param.Draw(width); } } ImGui.Separator(); ImGui.Text($"Address: 0x{sLightProbes!.Instance:X}"); foreach (Parameter param in lightProbesParameters) { param.Draw(width); } } ImGui.PopID(); ImGui.PushID("Bloom"); if (ImGui.CollapsingHeader("Bloom")) { if (bloomObject != 0x0) { ImGui.Text($"Address: 0x{bloomObject:X}"); foreach (Parameter param in bloomParameters) { param.Draw(width); } } } ImGui.PopID(); ImGui.PushID("DepthOfField"); if (ImGui.CollapsingHeader("Depth of Field")) { if (dofObject != 0x0) { ImGui.Text($"Address: 0x{dofObject:X}"); foreach (Parameter param in dofParameters) { param.Draw(width); } } } ImGui.PopID(); ImGui.PushID("MotionBlur"); if (ImGui.CollapsingHeader("Motion Blur")) { if (motionBlurObject != 0x0) { ImGui.Text($"Address: 0x{motionBlurObject:X}"); foreach (Parameter param in motionBlurParameters) { param.Draw(width); } } } ImGui.PopID(); ImGui.PushID("FXAA"); if (ImGui.CollapsingHeader("FXAA")) { if (fxaaObject != 0x0) { ImGui.Text($"Address: 0x{fxaaObject:X}"); foreach (Parameter param in fxaaParameters) { param.Draw(width); } } } ImGui.PopID(); ImGui.Separator(); } if (ImGui.CollapsingHeader("DEBUG")) { ImGui.Checkbox("Globally Ignore Parameter Min/Max", ref ignoreMinMax); if (ImGui.BeginItemTooltip()) { ImGui.Text("Some min/max values are set to avoid crashes, so be warned."); ImGui.EndTooltip(); } 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(); } } #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 } }