Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ValheimVisuals v1.4.9
plugins/ValheimVisuals.dll
Decompiled 2 days ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.PostProcessing; using UnityEngine.Rendering; using UnityEngine.UI; using UnityStandardAssets.ImageEffects; using ValheimMods.Shared; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: InternalsVisibleTo("ValheimVisuals.Checks")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ValheimVisuals")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.4.9.0")] [assembly: AssemblyInformationalVersion("1.4.9+24ce96994953df6fa3721ebd878bf87486a95c96")] [assembly: AssemblyProduct("ValheimVisuals")] [assembly: AssemblyTitle("ValheimVisuals")] [assembly: AssemblyVersion("1.4.9.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ValheimMods.Shared { internal sealed class ScreenPass { public readonly string Name; private static readonly string Owner = typeof(ScreenPass).Assembly.GetName().Name; public readonly int Order; public readonly List<KeyValuePair<CameraEvent, CommandBuffer>> CameraBuffers = new List<KeyValuePair<CameraEvent, CommandBuffer>>(); public readonly List<KeyValuePair<LightEvent, CommandBuffer>> SunBuffers = new List<KeyValuePair<LightEvent, CommandBuffer>>(); public Action<Camera, Light> Frame; public ScreenPass(string name, int order) { Name = name; Order = order; } public CommandBuffer OnCamera(CameraEvent at) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) CommandBuffer val = new CommandBuffer { name = Owner + " " + Name }; CameraBuffers.Add(new KeyValuePair<CameraEvent, CommandBuffer>(at, val)); return val; } public CommandBuffer OnSun(LightEvent at) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0027: Unknown result type (might be due to invalid IL or missing references) CommandBuffer val = new CommandBuffer { name = Owner + " " + Name }; SunBuffers.Add(new KeyValuePair<LightEvent, CommandBuffer>(at, val)); return val; } public void Release() { foreach (KeyValuePair<CameraEvent, CommandBuffer> cameraBuffer in CameraBuffers) { cameraBuffer.Value.Release(); } foreach (KeyValuePair<LightEvent, CommandBuffer> sunBuffer in SunBuffers) { sunBuffer.Value.Release(); } CameraBuffers.Clear(); SunBuffers.Clear(); } } internal static class ScreenPasses { [CompilerGenerated] private static class <>O { public static CameraCallback <0>__OnPreCull; public static CameraCallback <1>__OnPostRender; } private static readonly int RayBl = Shader.PropertyToID("_VV_RayBL"); private static readonly int RayBr = Shader.PropertyToID("_VV_RayBR"); private static readonly int RayTl = Shader.PropertyToID("_VV_RayTL"); private static readonly int RayTr = Shader.PropertyToID("_VV_RayTR"); private static readonly int WorldToView = Shader.PropertyToID("_VV_WorldToView"); private static readonly int TanHalf = Shader.PropertyToID("_VV_TanHalf"); private static readonly int SunDir = Shader.PropertyToID("_VV_SunDir"); private static readonly List<ScreenPass> Active = new List<ScreenPass>(); private static readonly Vector3[] Corners = (Vector3[])(object)new Vector3[4]; private static readonly Matrix4x4 FlipZ = Matrix4x4.Scale(new Vector3(1f, 1f, -1f)); private static bool _hooked; private static bool _attached; private static Camera _camera; private static Light _sun; private static bool _addedDepth; private static bool _sunOn; private static readonly List<ScreenPass> Known = new List<ScreenPass>(); public static void Enable(ScreenPass pass) { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown if (Active.Contains(pass)) { return; } Active.Add(pass); Active.Sort((ScreenPass a, ScreenPass b) => a.Order.CompareTo(b.Order)); Detach(); if (!_hooked) { CameraCallback onPreCull = Camera.onPreCull; object obj = <>O.<0>__OnPreCull; if (obj == null) { CameraCallback val = OnPreCull; <>O.<0>__OnPreCull = val; obj = (object)val; } Camera.onPreCull = (CameraCallback)Delegate.Combine((Delegate?)(object)onPreCull, (Delegate?)obj); CameraCallback onPostRender = Camera.onPostRender; object obj2 = <>O.<1>__OnPostRender; if (obj2 == null) { CameraCallback val2 = OnPostRender; <>O.<1>__OnPostRender = val2; obj2 = (object)val2; } Camera.onPostRender = (CameraCallback)Delegate.Combine((Delegate?)(object)onPostRender, (Delegate?)obj2); _hooked = true; } } public static void Disable(ScreenPass pass) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Expected O, but got Unknown if (!Active.Remove(pass)) { return; } Detach(); if (Active.Count == 0 && _hooked) { CameraCallback onPreCull = Camera.onPreCull; object obj = <>O.<0>__OnPreCull; if (obj == null) { CameraCallback val = OnPreCull; <>O.<0>__OnPreCull = val; obj = (object)val; } Camera.onPreCull = (CameraCallback)Delegate.Remove((Delegate?)(object)onPreCull, (Delegate?)obj); CameraCallback onPostRender = Camera.onPostRender; object obj2 = <>O.<1>__OnPostRender; if (obj2 == null) { CameraCallback val2 = OnPostRender; <>O.<1>__OnPostRender = val2; obj2 = (object)val2; } Camera.onPostRender = (CameraCallback)Delegate.Remove((Delegate?)(object)onPostRender, (Delegate?)obj2); _hooked = false; } } private static void OnPreCull(Camera cam) { //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Invalid comparison between Unknown and I4 if ((Object)(object)cam == (Object)null) { return; } if ((Object)(object)cam != (Object)(object)Camera.main) { SunBuffers(on: false); return; } Light val = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.m_dirLight : null); if ((Object)(object)ZoneSystem.instance == (Object)null || (Object)(object)val == (Object)null || (int)cam.actualRenderingPath != 3) { Detach(); return; } if (!_attached || (Object)(object)cam != (Object)(object)_camera || (Object)(object)val != (Object)(object)_sun) { Detach(); Attach(cam, val); } SunBuffers(on: true); SetGlobals(cam, val); foreach (ScreenPass item in Active) { item.Frame?.Invoke(cam, val); } } private static void SetGlobals(Camera cam, Light sun) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) cam.CalculateFrustumCorners(new Rect(0f, 0f, 1f, 1f), 1f, (MonoOrStereoscopicEye)2, Corners); Transform transform = ((Component)cam).transform; Shader.SetGlobalVector(RayBl, Vector4.op_Implicit(transform.TransformVector(Corners[0]))); Shader.SetGlobalVector(RayTl, Vector4.op_Implicit(transform.TransformVector(Corners[1]))); Shader.SetGlobalVector(RayTr, Vector4.op_Implicit(transform.TransformVector(Corners[2]))); Shader.SetGlobalVector(RayBr, Vector4.op_Implicit(transform.TransformVector(Corners[3]))); Shader.SetGlobalMatrix(WorldToView, FlipZ * cam.worldToCameraMatrix); float num = Mathf.Tan(cam.fieldOfView * 0.5f * (MathF.PI / 180f)); Shader.SetGlobalVector(TanHalf, new Vector4(num * cam.aspect, num, (float)cam.pixelWidth, (float)cam.pixelHeight)); Shader.SetGlobalVector(SunDir, Vector4.op_Implicit(-((Component)sun).transform.forward)); } private static void Attach(Camera cam, Light sun) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) _camera = cam; _sun = sun; _addedDepth = (cam.depthTextureMode & 1) == 0; cam.depthTextureMode = (DepthTextureMode)(cam.depthTextureMode | 1); foreach (ScreenPass item in Active) { foreach (KeyValuePair<CameraEvent, CommandBuffer> cameraBuffer in item.CameraBuffers) { cam.AddCommandBuffer(cameraBuffer.Key, cameraBuffer.Value); } foreach (KeyValuePair<LightEvent, CommandBuffer> sunBuffer in item.SunBuffers) { sun.AddCommandBuffer(sunBuffer.Key, sunBuffer.Value); } } _attached = true; _sunOn = true; } private static void OnPostRender(Camera cam) { if ((Object)(object)cam != (Object)null && (Object)(object)cam != (Object)(object)Camera.main) { SunBuffers(on: true); } } private static void SunBuffers(bool on) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) if (!_attached || on == _sunOn || (Object)(object)_sun == (Object)null) { return; } _sunOn = on; foreach (ScreenPass item in on ? Active : Known) { foreach (KeyValuePair<LightEvent, CommandBuffer> sunBuffer in item.SunBuffers) { if (on) { _sun.AddCommandBuffer(sunBuffer.Key, sunBuffer.Value); } else { _sun.RemoveCommandBuffer(sunBuffer.Key, sunBuffer.Value); } } } } private static void Detach() { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_camera != (Object)null) { foreach (ScreenPass item in AllKnown()) { foreach (KeyValuePair<CameraEvent, CommandBuffer> cameraBuffer in item.CameraBuffers) { _camera.RemoveCommandBuffer(cameraBuffer.Key, cameraBuffer.Value); } } if (_addedDepth) { Camera camera = _camera; camera.depthTextureMode = (DepthTextureMode)(camera.depthTextureMode & -2); } } if ((Object)(object)_sun != (Object)null) { foreach (ScreenPass item2 in AllKnown()) { foreach (KeyValuePair<LightEvent, CommandBuffer> sunBuffer in item2.SunBuffers) { _sun.RemoveCommandBuffer(sunBuffer.Key, sunBuffer.Value); } } } _camera = null; _sun = null; _addedDepth = false; _attached = false; _sunOn = false; } public static void Register(ScreenPass pass) { if (!Known.Contains(pass)) { Known.Add(pass); } } public static void Unregister(ScreenPass pass) { Disable(pass); Detach(); Known.Remove(pass); } private static IEnumerable<ScreenPass> AllKnown() { return Known; } } internal static class GrassRadiusClaims { private const int Version = 1; private const string Slot = "ValheimMods.Shared.GrassRadius"; private static ManualLogSource _log; private static readonly Dictionary<string, KeyValuePair<int, float>> Claims = new Dictionary<string, KeyValuePair<int, float>>(); private static ClutterSystem _on; private static ClutterSystem _aside; private static float _found; private static float _written; private static string _asideHolder; private static float _asideRadius; internal static void Offer(string owner) { SharedElection.Offers("ValheimMods.Shared.GrassRadius").Add(new object[3] { 1, owner, new Action(Install) }); } internal static bool Offered(string owner) { return SharedElection.Offers("ValheimMods.Shared.GrassRadius").Exists((object[] o) => owner.Equals(o[1])); } internal static void Claim(string holder, int rank, float radius) { object[] array = Calls(); if (array != null) { ((Action<string, int, float>)array[0])(holder, rank, radius); } } internal static void Release(string holder) { object[] array = Calls(); if (array != null) { ((Action<string>)array[1])(holder); } } private static object[] Calls() { if (AppDomain.CurrentDomain.GetData("ValheimMods.Shared.GrassRadius.calls") is object[] result) { return result; } SharedElection.Elect("ValheimMods.Shared.GrassRadius"); return AppDomain.CurrentDomain.GetData("ValheimMods.Shared.GrassRadius.calls") as object[]; } private static void Install() { _log = Logger.CreateLogSource("Grass radius"); AppDomain.CurrentDomain.SetData("ValheimMods.Shared.GrassRadius.calls", new object[2] { new Action<string, int, float>(ClaimHere), new Action<string>(ReleaseHere) }); } private static void ClaimHere(string holder, int rank, float radius) { Claims[holder] = new KeyValuePair<int, float>(rank, radius); Settle(); } private static void ReleaseHere(string holder) { Claims.Remove(holder); Settle(); } private static void Settle() { //IL_00b6: Unknown result type (might be due to invalid IL or missing references) ClutterSystem instance = ClutterSystem.instance; if (_on != null && _on != instance) { GrassLimits.Restore(); _on = null; } if ((Object)(object)instance == (Object)null) { return; } float radius; string text = InEffect(out radius); if (text == null) { LetGo(instance); } else if (_aside != instance || !(text == _asideHolder) || radius != _asideRadius) { if (_on == null) { _on = instance; _found = instance.m_distance; } else if (!Mathf.Approximately(instance.m_distance, _written)) { StandAside(instance, text, radius); return; } bool num = !Mathf.Approximately(instance.m_distance, radius); instance.m_distance = radius; _written = radius; if (num | GrassLimits.Lift(instance)) { instance.ResetGrass(Vector3.zero, float.MaxValue); } } } private static string InEffect(out float radius) { string text = null; int num = 0; radius = 0f; foreach (KeyValuePair<string, KeyValuePair<int, float>> claim in Claims) { if (text == null || claim.Value.Key > num || (claim.Value.Key == num && string.CompareOrdinal(claim.Key, text) < 0)) { text = claim.Key; num = claim.Value.Key; radius = claim.Value.Value; } } return text; } private static void LetGo(ClutterSystem cs) { //IL_004d: Unknown result type (might be due to invalid IL or missing references) if (_on != null) { bool flag = false; if (Mathf.Approximately(cs.m_distance, _written)) { flag = !Mathf.Approximately(cs.m_distance, _found); cs.m_distance = _found; } flag |= GrassLimits.Restore(); _on = null; if (flag) { cs.ResetGrass(Vector3.zero, float.MaxValue); } } } private static void StandAside(ClutterSystem cs, string holder, float radius) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) _log.LogWarning((object)$"Another mod sets the grass radius ({cs.m_distance:F0} m now, {_written:F0} m set here for {holder}): left to it for this world, or until another radius is asked for."); if (GrassLimits.Restore()) { cs.ResetGrass(Vector3.zero, float.MaxValue); } _on = null; _aside = cs; _asideHolder = holder; _asideRadius = radius; } } internal static class GrassLimits { internal const float NoLimit = 999f; private const float LeastLimit = 90f; private const int MostVertices = 200; private static readonly Dictionary<InstanceRenderer, float> Limits = new Dictionary<InstanceRenderer, float>(); internal static bool Lift(ClutterSystem cs) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) bool result = false; foreach (Clutter item in cs.m_clutter) { InstanceRenderer val = (((Object)(object)item.m_prefab != (Object)null) ? item.m_prefab.GetComponent<InstanceRenderer>() : null); if (!((Object)(object)val == (Object)null) && val.m_useLod && !Limits.ContainsKey(val) && !(val.m_lodMaxDistance < 90f) && !(val.m_lodMaxDistance >= 999f) && !((Object)(object)val.m_mesh == (Object)null) && val.m_mesh.vertexCount <= 200 && (int)val.m_shadowCasting == 0) { Limits[val] = val.m_lodMaxDistance; val.m_lodMaxDistance = 999f; result = true; } } return result; } internal static bool Restore() { bool result = Limits.Count > 0; foreach (KeyValuePair<InstanceRenderer, float> limit in Limits) { if ((Object)(object)limit.Key != (Object)null) { limit.Key.m_lodMaxDistance = limit.Value; } } Limits.Clear(); return result; } } internal static class SharedElection { internal const string Nobody = "nobody"; internal static List<object[]> Offers(string slot) { List<object[]> list = AppDomain.CurrentDomain.GetData(slot) as List<object[]>; if (list == null) { AppDomain.CurrentDomain.SetData(slot, list = new List<object[]>()); } return list; } internal static void Elect(string slot, int runsAt = -1, Func<object[], object[], bool> better = null) { List<object[]> list = Offers(slot); if (list.Count != 0 && AppDomain.CurrentDomain.GetData(slot + ".elected") == null) { Install(slot, Best(list, runsAt, better)); } } internal static bool HandedOn(string slot, Type copy, int runsAt, Func<object[], object[], bool> better = null) { List<object[]> list = Offers(slot); object[] array = list.Find((object[] o) => o[2] is Delegate obj && obj.Method.DeclaringType == copy); if (array == null || Runs(array, runsAt)) { return false; } Install(slot, Best(list, runsAt, better)); return true; } private static void Install(string slot, object[] best) { AppDomain.CurrentDomain.SetData(slot + ".elected", (best != null) ? best[1] : "nobody"); if (best != null) { ((Action)best[2])(); } } private static object[] Best(List<object[]> offers, int runsAt, Func<object[], object[], bool> better) { object[] array = null; foreach (object[] offer in offers) { if (Runs(offer, runsAt) && (array == null || (int)offer[0] > (int)array[0] || ((int)offer[0] == (int)array[0] && better != null && better(offer, array)))) { array = offer; } } return array; } private static bool Runs(object[] offer, int runsAt) { if (runsAt < 0 || offer.Length <= runsAt) { return true; } try { return !(offer[runsAt] is Func<bool> func) || func(); } catch { return false; } } } } namespace ValheimVisuals { internal static class CasSharpen { private static readonly int PeakId = Shader.PropertyToID("_VV_CasPeak"); private static readonly int SourceId = Shader.PropertyToID("_VV_CasSource"); private static PresetValues _v; private static bool _attempted; private static Material _material; private static ScreenPass _pass; public static void Apply(PresetValues v) { _v = v; if (!v.PostFxEnabled || !v.CasEnabled || !EnsurePass()) { Restore(); } else { ScreenPasses.Enable(_pass); } } private static bool EnsurePass() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) if (_pass != null) { return true; } if (_attempted) { return false; } _attempted = true; Shader val = ShaderBundle.Load<Shader>("Assets/Shaders/CasSharpen.shader"); if ((Object)(object)val == (Object)null || !val.isSupported) { VisualsPlugin.Log.LogWarning((object)"[sharpen] shader unavailable on this platform; TAA keeps its own sharpen."); return false; } _material = new Material(val) { name = "ValheimVisuals CasSharpen", hideFlags = (HideFlags)61 }; _pass = new ScreenPass("contrast-adaptive sharpen", 50) { Frame = Frame }; CommandBuffer obj = _pass.OnCamera((CameraEvent)19); obj.GetTemporaryRT(SourceId, -1, -1, 0, (FilterMode)0); obj.Blit(RenderTargetIdentifier.op_Implicit((BuiltinRenderTextureType)2), RenderTargetIdentifier.op_Implicit(SourceId)); obj.Blit(RenderTargetIdentifier.op_Implicit(SourceId), RenderTargetIdentifier.op_Implicit((BuiltinRenderTextureType)2), _material, 0); obj.ReleaseTemporaryRT(SourceId); ScreenPasses.Register(_pass); return true; } private static void Frame(Camera cam, Light sun) { _material.SetFloat(PeakId, -1f / Mathf.Lerp(8f, 5f, Mathf.Clamp01(_v.CasSharpness))); } public static bool ReplacesTaaSharpen(PresetValues v) { if (v.PostFxEnabled && v.CasEnabled) { return _pass != null; } return false; } public static void Restore() { if (_pass != null) { ScreenPasses.Disable(_pass); } } public static void Shutdown() { Restore(); if (_pass != null) { ScreenPasses.Unregister(_pass); _pass.Release(); } _pass = null; if ((Object)(object)_material != (Object)null) { Object.Destroy((Object)(object)_material); } _material = null; _attempted = false; } } internal static class Caustics { private const string ShaderAsset = "Assets/Shaders/Caustics.shader"; private const string FieldShaderAsset = "Assets/Shaders/CausticsField.shader"; private const float FadeInDepth = 0.3f; private const float FarDistance = 200f; private const float WaveRoom = 10f; private const int FieldSize = 512; private const int GridCells = 480; private const float Margin = 0.15f; private const int Ripples = 24; internal static readonly Vector4 Depths = new Vector4(0.5f, 1.2f, 2.5f, 5f); private static readonly int WaterId = Shader.PropertyToID("_VV_Water"); private static readonly int PatternId = Shader.PropertyToID("_VV_Pattern"); private static readonly int SunColorId = Shader.PropertyToID("_VV_SunColor"); private static readonly int CookieId = Shader.PropertyToID("_VV_Cookie"); private static readonly int CookieMatrixId = Shader.PropertyToID("_VV_CookieMatrix"); private static readonly int MaskId = Shader.PropertyToID("_VV_ShadowMask"); private static readonly int MaskValidId = Shader.PropertyToID("_VV_ShadowValid"); private static readonly int FieldId = Shader.PropertyToID("_VW_CausticField"); private static readonly int FieldSizeId = Shader.PropertyToID("_VW_CausticFieldSize"); private static readonly int FieldDepthsId = Shader.PropertyToID("_VW_FieldDepths"); private static readonly int RipplesId = Shader.PropertyToID("_VW_Ripples"); private static readonly int LightId = Shader.PropertyToID("_VW_RippleLight"); private static readonly int OffsetId = Shader.PropertyToID("_Offset"); internal static bool DebugView; internal static int Repeats = 1; private static PresetValues _v; private static bool _attempted; private static Material _material; private static Material _fieldMaterial; private static RenderTexture _field; private static Mesh _grid; private static ScreenPass _pass; private static CommandBuffer _draw; private static CommandBuffer _fieldDraw; private static int _builtFor = -1; private static float _builtSize = -1f; private static readonly Vector4[] Waves = (Vector4[])(object)new Vector4[24]; private static readonly double[] Omega = new double[24]; private static readonly Vector4[] Current = (Vector4[])(object)new Vector4[24]; private static double _time; internal const float FullLightLuminance = 2.5f; public static void Apply(PresetValues v) { _v = v; if (!v.CausticsEnabled || !EnsurePass()) { Restore(); } else { ScreenPasses.Enable(_pass); } } private static bool EnsurePass() { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Expected O, but got Unknown //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0136: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) if (_pass != null) { return true; } if (_attempted) { return false; } _attempted = true; Shader val = ShaderBundle.Load<Shader>("Assets/Shaders/Caustics.shader"); Shader val2 = ShaderBundle.Load<Shader>("Assets/Shaders/CausticsField.shader"); if ((Object)(object)val == (Object)null || !val.isSupported || (Object)(object)val2 == (Object)null || !val2.isSupported) { VisualsPlugin.Log.LogWarning((object)"[caustics] shader unavailable on this platform; caustics stay off."); return false; } _material = new Material(val) { name = "ValheimVisuals Caustics", hideFlags = (HideFlags)61 }; _fieldMaterial = new Material(val2) { name = "ValheimVisuals Caustic Field", hideFlags = (HideFlags)61 }; _field = new RenderTexture(512, 512, 0, (RenderTextureFormat)2, (RenderTextureReadWrite)1) { name = "ValheimVisuals caustic field", useMipMap = true, autoGenerateMips = false, wrapMode = (TextureWrapMode)0, filterMode = (FilterMode)2, anisoLevel = 4, hideFlags = (HideFlags)61 }; _field.Create(); _grid = BuildGrid(); MakeRipples(Waves); Shader.SetGlobalTexture(FieldId, (Texture)(object)_field); Shader.SetGlobalVector(FieldSizeId, new Vector4(512f, 0f)); Shader.SetGlobalVector(FieldDepthsId, Depths); _pass = new ScreenPass("caustics", 20) { Frame = Frame }; _fieldDraw = _pass.OnCamera((CameraEvent)4); CommandBuffer obj = _pass.OnCamera((CameraEvent)6); obj.SetGlobalTexture(MaskId, RenderTargetIdentifier.op_Implicit((Texture)(object)Texture2D.whiteTexture)); obj.SetGlobalFloat(MaskValidId, 0f); CommandBuffer obj2 = _pass.OnSun((LightEvent)3); obj2.GetTemporaryRT(MaskId, -1, -1, 0, (FilterMode)0, (RenderTextureFormat)16, (RenderTextureReadWrite)1); obj2.Blit(RenderTargetIdentifier.op_Implicit((BuiltinRenderTextureType)1), RenderTargetIdentifier.op_Implicit(MaskId)); obj2.SetGlobalFloat(MaskValidId, 1f); _draw = _pass.OnCamera((CameraEvent)7); ScreenPasses.Register(_pass); return true; } internal static Mesh BuildGrid() { //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_013a: Unknown result type (might be due to invalid IL or missing references) //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Expected O, but got Unknown int num = 481; Vector3[] array = (Vector3[])(object)new Vector3[num * num]; float num2 = 1.3f; for (int i = 0; i < num; i++) { for (int j = 0; j < num; j++) { array[i * num + j] = new Vector3(-0.15f + num2 * (float)j / 480f, -0.15f + num2 * (float)i / 480f, 0f); } } int[] array2 = new int[1382400]; int num3 = 0; for (int k = 0; k < 480; k++) { for (int l = 0; l < 480; l++) { int num4 = k * num + l; array2[num3++] = num4; array2[num3++] = num4 + num; array2[num3++] = num4 + 1; array2[num3++] = num4 + 1; array2[num3++] = num4 + num; array2[num3++] = num4 + num + 1; } } Mesh val = new Mesh { name = "ValheimVisuals caustic rays", indexFormat = (IndexFormat)1, hideFlags = (HideFlags)61, vertices = array }; val.SetIndices(array2, (MeshTopology)0, 0); val.bounds = new Bounds(Vector3.zero, Vector3.one * 10000f); return val; } internal static void MakeRipples(Vector4[] waves) { //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) Random random = new Random(20261009); int num = 0; while (num < waves.Length) { int num2 = random.Next(-14, 15); int num3 = random.Next(-14, 15); float num4 = Mathf.Sqrt((float)(num2 * num2 + num3 * num3)); if (num4 < 2f || num4 > 14f) { continue; } bool flag = false; for (int i = 0; i < num; i++) { if ((waves[i].x == (float)num2 && waves[i].y == (float)num3) || (waves[i].x == (float)(-num2) && waves[i].y == (float)(-num3))) { flag = true; } } if (!flag) { float num5 = (0.6f + 0.4f * (float)random.NextDouble()) / (num4 * num4); waves[num] = new Vector4((float)num2, (float)num3, num5, (float)(random.NextDouble() * Math.PI * 2.0)); num++; } } } internal static float BaseSlope(Vector4[] waves) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) double num = 0.0; foreach (Vector4 val in waves) { double num2 = (double)(val.z * 2f) * Math.PI * Math.Sqrt(val.x * val.x + val.y * val.y); num += num2 * num2 / 2.0; } return (float)Math.Sqrt(num); } internal static float SlopeFor(float wind) { return Mathf.Lerp(0.04f, 0.09f, Mathf.Clamp01(wind)); } private static void Frame(Camera cam, Light sun) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_02a9: Unknown result type (might be due to invalid IL or missing references) //IL_02d2: Unknown result type (might be due to invalid IL or missing references) //IL_02d7: Unknown result type (might be due to invalid IL or missing references) //IL_02dc: Unknown result type (might be due to invalid IL or missing references) //IL_02e3: Unknown result type (might be due to invalid IL or missing references) //IL_02eb: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) //IL_030a: Unknown result type (might be due to invalid IL or missing references) //IL_030f: Unknown result type (might be due to invalid IL or missing references) //IL_0313: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0351: Unknown result type (might be due to invalid IL or missing references) //IL_0385: Unknown result type (might be due to invalid IL or missing references) //IL_0399: Unknown result type (might be due to invalid IL or missing references) //IL_039a: Unknown result type (might be due to invalid IL or missing references) //IL_0404: Unknown result type (might be due to invalid IL or missing references) //IL_0409: Unknown result type (might be due to invalid IL or missing references) //IL_0414: Unknown result type (might be due to invalid IL or missing references) //IL_0419: Unknown result type (might be due to invalid IL or missing references) _time += Time.deltaTime * _v.CausticsSpeed; Color val = (((Behaviour)sun).isActiveAndEnabled ? SunColor(sun) : Color.black); val *= Visibility(val); float num = GameWaterSurface.Level(((Component)cam).transform.position, ZoneSystem.instance.m_waterLevel); if (!DebugView && Repeats <= 1 && (_v.CausticsStrength <= 0f || val.r + val.g + val.b <= 1E-05f || ((Component)cam).transform.position.y - num - 10f >= 200f)) { if (_builtFor != -1) { _draw.Clear(); _fieldDraw.Clear(); _builtFor = -1; } return; } float num2 = Mathf.Max(0.5f, _v.CausticsSize); int num3 = (DebugView ? 2 : ((!cam.allowHDR) ? 1 : 0)); int num4 = Mathf.Max(1, Repeats); int num5 = num3 + 4 * num4; if (num5 != _builtFor || num2 != _builtSize) { _builtFor = num5; _builtSize = num2; BuildField(num2, num4); _draw.Clear(); for (int i = 0; i < num4; i++) { _draw.Blit((Texture)(object)Texture2D.blackTexture, RenderTargetIdentifier.op_Implicit((BuiltinRenderTextureType)2), _material, num3); } _draw.SetGlobalFloat(MaskValidId, 0f); } for (int j = 0; j < 24; j++) { Omega[j] = Math.Sqrt(61.638047863431744 * Math.Sqrt(Waves[j].x * Waves[j].x + Waves[j].y * Waves[j].y) / (double)num2); } float num6 = SlopeFor(((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.GetWindIntensity() : 0.3f) / BaseSlope(Waves); for (int k = 0; k < 24; k++) { double num7 = ((double)Waves[k].w - Omega[k] * _time) % (Math.PI * 2.0); Current[k] = new Vector4(Waves[k].x, Waves[k].y, Waves[k].z * num6, (float)num7); } Shader.SetGlobalVectorArray(RipplesId, Current); Vector3 val2 = -((Component)sun).transform.forward; int lightId = LightId; Vector4 val3 = new Vector4(0f - val2.x, 0f - val2.z, Mathf.Min(0f - val2.y, -0.05f), 0f); Shader.SetGlobalVector(lightId, ((Vector4)(ref val3)).normalized); _material.SetVector(WaterId, new Vector4(num, _v.CausticsMaxDepth, 0.3f, 200f)); GameWaterSurface.Bind(_material, ((Component)cam).transform.position); _material.SetVector(PatternId, new Vector4(1f / num2, 0f, _v.CausticsStrength, _v.CausticsColourSplit)); _material.SetVector(SunColorId, Color.op_Implicit(val)); Texture val4 = (((Behaviour)sun).isActiveAndEnabled ? sun.cookie : null); _material.SetTexture(CookieId, (Texture)(((Object)(object)val4 != (Object)null) ? ((object)val4) : ((object)Texture2D.whiteTexture))); float num8 = 1f / Mathf.Max(1f, sun.cookieSize); _material.SetMatrix(CookieMatrixId, Matrix4x4.Scale(new Vector3(num8, num8, 1f)) * ((Component)sun).transform.worldToLocalMatrix); } private static void BuildField(float size, int repeats) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) _fieldDraw.Clear(); _fieldDraw.SetRenderTarget(RenderTargetIdentifier.op_Implicit((Texture)(object)_field)); for (int i = 0; i < repeats; i++) { _fieldDraw.ClearRenderTarget(false, true, Color.clear); for (int j = 0; j < 4; j++) { MaterialPropertyBlock val = new MaterialPropertyBlock(); int offsetId = OffsetId; Vector4 depths = Depths; val.SetVector(offsetId, new Vector4(0f, 0f, ((Vector4)(ref depths))[j] / size, 0f)); _fieldDraw.DrawMesh(_grid, Matrix4x4.identity, _fieldMaterial, 0, j, val); } } _fieldDraw.GenerateMips(_field); } internal static float Visibility(Color linear) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) return Mathf.Clamp01((0.2126f * linear.r + 0.7152f * linear.g + 0.0722f * linear.b) / 2.5f); } internal static Color SunColor(Light sun) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) Color result; Color val; if ((int)QualitySettings.activeColorSpace != 1) { result = sun.color * sun.intensity; } else if (GraphicsSettings.lightsUseLinearIntensity) { val = sun.color; result = ((Color)(ref val)).linear * sun.intensity; } else { val = sun.color * sun.intensity; result = ((Color)(ref val)).linear; } result.a = 1f; return result; } public static void Restore() { if (_pass != null) { ScreenPasses.Disable(_pass); } } public static void Shutdown() { if (_pass != null) { ScreenPasses.Unregister(_pass); _pass.Release(); } _pass = null; _draw = (_fieldDraw = null); _builtFor = -1; _builtSize = -1f; if ((Object)(object)_material != (Object)null) { Object.Destroy((Object)(object)_material); } if ((Object)(object)_fieldMaterial != (Object)null) { Object.Destroy((Object)(object)_fieldMaterial); } if ((Object)(object)_grid != (Object)null) { Object.Destroy((Object)(object)_grid); } if ((Object)(object)_field != (Object)null) { _field.Release(); Object.Destroy((Object)(object)_field); } GameWaterSurface.Release(); _material = (_fieldMaterial = null); _grid = null; _field = null; _attempted = false; } } internal static class CloudShadows { private const string ShaderAsset = "Assets/Shaders/CloudShadows.shader"; internal const int ProfileSamples = 33; private static readonly int CloudShadowId = Shader.PropertyToID("_VV_CloudShadow"); private static readonly int DomeId = Shader.PropertyToID("_VV_Dome"); private static readonly int DomeSpanId = Shader.PropertyToID("_VV_DomeSpan"); private static readonly int ProfileId = Shader.PropertyToID("_VV_Profile"); private static readonly int GameMainTex = Shader.PropertyToID("_MainTex"); private static readonly int CloudTexId = Shader.PropertyToID("_VV_CloudTex"); private static readonly int[] Textures = new int[2] { Shader.PropertyToID("_RainTex"), Shader.PropertyToID("_RefractionNormal") }; private static readonly int[] Floats = new int[8] { Shader.PropertyToID("_UVScale"), Shader.PropertyToID("_Opacity"), Shader.PropertyToID("_Rain"), Shader.PropertyToID("_RainUVScale"), Shader.PropertyToID("_RainOpacity"), Shader.PropertyToID("_RefractionScale"), Shader.PropertyToID("_RefractionIntensity"), Shader.PropertyToID("_RefractionSpeed") }; private static readonly int RainId = Shader.PropertyToID("_Rain"); private static PresetValues _v; private static bool _attempted; private static Material _material; private static ScreenPass _pass; private static CommandBuffer _draw; private static int _builtFor; private static Mesh _profiled; private static readonly float[] Profile = new float[33]; private static float _profileTop; private static float _profileBottom; private static float _meshRadius = 1f; internal static int Repeats = 1; internal static bool Applied => _builtFor > 0; public static void Apply(PresetValues v) { _v = v; if (!v.CloudShadowsEnabled || !EnsurePass()) { Restore(); } else { ScreenPasses.Enable(_pass); } } private static bool EnsurePass() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown if (_pass != null) { return true; } if (_attempted) { return false; } _attempted = true; Shader val = ShaderBundle.Load<Shader>("Assets/Shaders/CloudShadows.shader"); if ((Object)(object)val == (Object)null || !val.isSupported) { VisualsPlugin.Log.LogWarning((object)"[cloud shadows] shader unavailable on this platform; cloud shadows stay off."); return false; } _material = new Material(val) { name = "ValheimVisuals Cloud Shadows", hideFlags = (HideFlags)61 }; _pass = new ScreenPass("cloud shadows", 5) { Frame = Frame }; _draw = _pass.OnSun((LightEvent)3); ScreenPasses.Register(_pass); return true; } private static void Frame(Camera cam, Light sun) { //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_019b: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c8: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_020a: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) MeshRenderer val = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.m_clouds : null); Material val2 = (((Object)(object)val != (Object)null) ? ((Renderer)val).sharedMaterial : null); MeshFilter val3 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<MeshFilter>() : null); int num; float num2; if ((Object)(object)val != (Object)null && ((Renderer)val).enabled && ((Component)val).gameObject.activeInHierarchy && (Object)(object)val2 != (Object)null && (Object)(object)val3 != (Object)null) { num = (((Object)(object)val3.sharedMesh != (Object)null) ? 1 : 0); if (num != 0 && val2.HasProperty(RainId)) { num2 = val2.GetFloat(RainId); goto IL_00a2; } } else { num = 0; } num2 = 0f; goto IL_00a2; IL_00a2: float num3 = num2; float num4 = Mathf.Clamp01(_v.CloudShadowStrength) * (1f - Mathf.Clamp01(num3)); if (num == 0 || num4 <= 0.001f) { Stop(); return; } _material.SetTexture(CloudTexId, val2.HasProperty(GameMainTex) ? val2.GetTexture(GameMainTex) : null); int[] textures = Textures; foreach (int num5 in textures) { _material.SetTexture(num5, val2.HasProperty(num5) ? val2.GetTexture(num5) : null); } textures = Floats; foreach (int num6 in textures) { _material.SetFloat(num6, val2.HasProperty(num6) ? val2.GetFloat(num6) : 0f); } UpdateProfile(val3.sharedMesh); Transform transform = ((Component)val).transform; Vector3 position = transform.position; Vector3 lossyScale = transform.lossyScale; _material.SetVector(DomeId, new Vector4(position.x, position.y, position.z, Mathf.Max(1f, lossyScale.x * _meshRadius))); _material.SetVector(DomeSpanId, new Vector4(lossyScale.y, position.y + lossyScale.y * _profileTop, position.y + lossyScale.y * _profileBottom, 0f)); _material.SetFloatArray(ProfileId, Profile); _material.SetVector(CloudShadowId, new Vector4(num4, 0f, 0f, 0f)); int num7 = Mathf.Max(1, Repeats); if (num7 != _builtFor) { _builtFor = num7; _draw.Clear(); for (int j = 0; j < num7; j++) { _draw.Blit((Texture)(object)Texture2D.blackTexture, RenderTargetIdentifier.op_Implicit((BuiltinRenderTextureType)1), _material, 0); } } } private static void UpdateProfile(Mesh mesh) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)mesh == (Object)(object)_profiled) { return; } _profiled = mesh; Bounds bounds = mesh.bounds; _meshRadius = Mathf.Max(0.001f, Mathf.Max(((Bounds)(ref bounds)).extents.x, ((Bounds)(ref bounds)).extents.z)); if (!mesh.isReadable) { for (int i = 0; i < 33; i++) { float num = (float)i / 32f; Profile[i] = Mathf.Lerp(((Bounds)(ref bounds)).max.y, ((Bounds)(ref bounds)).min.y, num * num); } } else { _meshRadius = DomeProfile.Build(mesh.vertices, Profile); } _profileTop = float.MinValue; _profileBottom = float.MaxValue; float[] profile = Profile; foreach (float num2 in profile) { _profileTop = Mathf.Max(_profileTop, num2); _profileBottom = Mathf.Min(_profileBottom, num2); } _profileTop += 0.01f; _profileBottom -= 0.01f; VisualsPlugin.Log.LogInfo((object)$"[cloud shadows] cloud dome {((Object)mesh).name}: {mesh.vertexCount} vertices, height {Profile[0]:F3} in the middle, {Profile[16]:F3} half way, {Profile[32]:F3} at the rim (mesh units)."); } private static void Stop() { if (_builtFor != 0) { _draw.Clear(); _builtFor = 0; } } public static void Restore() { Stop(); if (_pass != null) { ScreenPasses.Disable(_pass); } } public static void Shutdown() { Restore(); if (_pass != null) { ScreenPasses.Unregister(_pass); _pass.Release(); } _pass = null; _draw = null; if ((Object)(object)_material != (Object)null) { Object.Destroy((Object)(object)_material); } _material = null; _profiled = null; _attempted = false; } } internal static class DomeProfile { public static float Build(Vector3[] vertices, float[] profile) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) int num = profile.Length; float num2 = 0f; Vector3[] array = vertices; foreach (Vector3 val in array) { num2 = Mathf.Max(num2, Mathf.Sqrt(val.x * val.x + val.z * val.z)); } if (num2 <= 0f) { for (int j = 0; j < num; j++) { profile[j] = 0f; } return 1f; } float[] array2 = new float[num]; int[] array3 = new int[num]; array = vertices; foreach (Vector3 val2 in array) { int num3 = Mathf.Clamp(Mathf.RoundToInt(Mathf.Sqrt(val2.x * val2.x + val2.z * val2.z) / num2 * (float)(num - 1)), 0, num - 1); array2[num3] += val2.y; array3[num3]++; } int num4 = -1; for (int k = 0; k < num; k++) { if (array3[k] == 0) { continue; } profile[k] = array2[k] / (float)array3[k]; if (num4 < 0) { for (int l = 0; l < k; l++) { profile[l] = profile[k]; } } else { for (int m = num4 + 1; m < k; m++) { profile[m] = Mathf.Lerp(profile[num4], profile[k], (float)(m - num4) / (float)(k - num4)); } } num4 = k; } for (int n = num4 + 1; n < num; n++) { profile[n] = profile[num4]; } return num2; } } internal class VisualsConfig { internal class ShadowsSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<bool> UseDistance; public readonly ConfigEntry<float> Distance; public readonly ConfigEntry<bool> UseCascades; public readonly ConfigEntry<int> Cascades; public readonly ConfigEntry<bool> UseCascadeSplit; public readonly ConfigEntry<float> CascadeSplit1; public readonly ConfigEntry<float> CascadeSplit2; public readonly ConfigEntry<float> CascadeSplit3; public readonly ConfigEntry<bool> UseResolution; public readonly ConfigEntry<ShadowResolution> Resolution; public readonly ConfigEntry<bool> UseProjection; public readonly ConfigEntry<ShadowProjection> Projection; public readonly ConfigEntry<bool> UseNearPlaneOffset; public readonly ConfigEntry<float> NearPlaneOffset; public readonly ConfigEntry<bool> UsePixelLightCount; public readonly ConfigEntry<int> PixelLightCount; public ShadowsSection(ConfigFile cfg, PresetValues p) { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) Enabled = Bind(cfg, "Shadows", "Enabled", def: true, "Whether this mod touches shadows at all. Off hands every value below back to the game's own graphics settings."); UseDistance = Bind(cfg, "Shadows", "UseDistance", p.UseShadowDistance, "First thing to turn off if distant ground and trees look flat and unshaded. The game uses 80/120/150 m for its three shadow steps; stretching that to 300 spreads the same shadow map over four times the area, and shadows too coarse to see read as no shadows at all."); Distance = Bind(cfg, "Shadows", "Distance", p.ShadowDistance, "How far shadows are drawn, in metres. Vanilla tops out at 150.", 20f, 600f); UseCascades = Bind(cfg, "Shadows", "UseCascades", p.UseShadowCascades, "Off leaves the cascade count the game chose (2, 3 or 4 by shadow quality)."); Cascades = Bind(cfg, "Shadows", "Cascades", p.ShadowCascades, "Shadow map cascades. More cascades spend the map more evenly over distance; only 1, 2 and 4 are meaningful to Unity.", 1, 4); UseCascadeSplit = Bind(cfg, "Shadows", "UseCascadeSplit", p.UseCascadeSplit, "Off restores Unity's own even-ish split. The game never writes this, so the mod owns it outright — and with Distance at 300 the front-loaded split below is what keeps near shadows sharp, so turning one off without the other changes the picture twice."); CascadeSplit1 = Bind(cfg, "Shadows", "CascadeSplit1", p.Cascade4Split.x, "End of the first cascade as a fraction of Distance. 0.04 of 300 m = 12 m.", 0.01f, 0.5f); CascadeSplit2 = Bind(cfg, "Shadows", "CascadeSplit2", p.Cascade4Split.y, "End of the second cascade. 0.12 of 300 m = 36 m.", 0.02f, 0.7f); CascadeSplit3 = Bind(cfg, "Shadows", "CascadeSplit3", p.Cascade4Split.z, "End of the third cascade; the fourth covers the rest. 0.32 of 300 m = 96 m, leaving 204 m for the last one — raise this if the middle distance is where things look unshaded.", 0.05f, 0.9f); UseResolution = Bind(cfg, "Shadows", "UseResolution", p.UseShadowResolution, "Off leaves the resolution the game picked (Low/Medium/High by shadow quality)."); Resolution = Bind<ShadowResolution>(cfg, "Shadows", "Resolution", p.Resolution, "Shadow map resolution. Vanilla's top step is High; VeryHigh is the one setting that directly buys back what a longer Distance spends."); UseProjection = Bind(cfg, "Shadows", "UseProjection", p.UseShadowProjection, "Off leaves the projection from the game's quality asset, which nothing in the game ever writes."); Projection = Bind<ShadowProjection>(cfg, "Shadows", "Projection", p.Projection, "StableFit stops shadow edges crawling as the camera moves; CloseFit is sharper but shimmers, and spends no resolution on staying stable — worth trying against a long Distance. Note that Valheim already uses StableFit, so leaving this at its default changes nothing."); UseNearPlaneOffset = Bind(cfg, "Shadows", "UseNearPlaneOffset", p.UseNearPlaneOffset, "Off leaves the game's own value."); NearPlaneOffset = Bind(cfg, "Shadows", "NearPlaneOffset", p.NearPlaneOffset, "Pulls the shadow camera back. Too small and objects lose the near end of their own shadow; too large and shadows detach from what casts them.", 0f, 10f); UsePixelLightCount = Bind(cfg, "Shadows", "UsePixelLightCount", p.UsePixelLightCount, "Off by default and normally right: the game derives this from its Lights setting (2, 4 or 8) and is already at the top step here, so writing anything could only lower it."); PixelLightCount = Bind(cfg, "Shadows", "PixelLightCount", p.PixelLightCount, "How many lights are computed per pixel rather than per vertex. Lights past the limit are not removed, only shaded more crudely — this changes precision, not how much light there is.", 1, 16); } } internal class SurfacesSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<bool> ForceAnisotropic; public readonly ConfigEntry<int> AnisoLimit; public readonly ConfigEntry<bool> UseMipmapLimit; public readonly ConfigEntry<int> MipmapLimit; public readonly ConfigEntry<bool> UseStreamingMipmapsMemoryBudget; public readonly ConfigEntry<float> StreamingMipmapsMemoryBudget; public readonly ConfigEntry<bool> FixSnowSpeckle; public SurfacesSection(ConfigFile cfg, PresetValues p) { Enabled = Bind(cfg, "Surfaces", "Enabled", def: true, "Texture filtering and resolution. Off hands both back to the game."); ForceAnisotropic = Bind(cfg, "Surfaces", "ForceAnisotropic", p.ForceAnisotropic, "Forces anisotropic filtering on every texture. The game can only switch anisotropy on and off and never chooses a level, so ground seen at a grazing angle is blurred for no reason but a missing setting."); AnisoLimit = Bind(cfg, "Surfaces", "AnisoLimit", p.AnisoForcedLimit, "Forced anisotropic level. Forcing the mode alone is not enough — without raising the limits the level stays whatever each texture was imported with, usually 1.", 1, 16); UseMipmapLimit = Bind(cfg, "Surfaces", "UseMipmapLimit", p.UseMipmapLimit, "Off leaves the game's own texture resolution setting alone."); MipmapLimit = Bind(cfg, "Surfaces", "MipmapLimit", p.MipmapLimit, "0 means full resolution. Each step up throws away the top mip of every texture.", 0, 3); UseStreamingMipmapsMemoryBudget = Bind(cfg, "Surfaces", "UseStreamingMipmapsMemoryBudget", p.UseStreamingMipmapsMemoryBudget, "For custom texture packs only. Vanilla Valheim currently has no loaded textures using Unity mipmap streaming, so this has no effect without a texture pack that explicitly enables streaming mipmaps. Off leaves the game's own budget alone."); StreamingMipmapsMemoryBudget = Bind(cfg, "Surfaces", "StreamingMipmapsMemoryBudget", p.StreamingMipmapsMemoryBudget, "Texture-mipmap streaming cache, in MiB, for a compatible custom texture pack. 4096 is approximately 4 GiB: a cache ceiling, not a VRAM reservation and not a texture-quality multiplier. Use only with spare VRAM.", 128f, 16384f); FixSnowSpeckle = Bind(cfg, "Surfaces", "FixSnowSpeckle", p.FixSnowSpeckle, "Fixes mountain snow breaking into small patches of shimmering noise. The game's terrain shader decides snow or rock per pixel from a value that rounding leaves at 0 or 0.0000001; this lifts the terrain's biome colours by 0.01% so it cannot. Off gives the game's meshes back unchanged."); } } internal class RenderingSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<bool> UseRenderScale; public readonly ConfigEntry<float> RenderScale; public readonly ConfigEntry<UpscaleFilter> Filter; public readonly ConfigEntry<bool> UseLodBias; public readonly ConfigEntry<float> LodBias; public readonly ConfigEntry<bool> UseTreeLod; public readonly ConfigEntry<float> TreeLodMultiplier; public RenderingSection(ConfigFile cfg, PresetValues p) { Enabled = Bind(cfg, "Rendering", "Enabled", def: true, "Render scale, upscaling and optional LOD controls. All overrides are off by default."); UseRenderScale = Bind(cfg, "Rendering", "UseRenderScale", p.UseRenderScale, "Use a custom internal resolution percentage instead of the game's fixed choices."); RenderScale = Bind(cfg, "Rendering", "RenderScale", p.RenderScale, "Percentage of screen width and height used for the 3D scene. UI stays at screen resolution. 100 is native; supersampling above 100 is not supported by the game's scaling path.", 25f, 100f); Filter = Bind(cfg, "Rendering", "Filter", p.UpscaleFilter, "Vanilla, Bilinear, Pixelated or Bicubic. Used only while the 3D scene renders below screen resolution, including the game's own render scale."); UseLodBias = Bind(cfg, "Rendering", "UseLodBias", p.UseLodBias, "Override the game's global LOD setting. Leave off when another mod manages LOD."); LodBias = Bind(cfg, "Rendering", "LodBias", p.LodBias, "Global LOD bias. Higher values retain detailed models farther away; does not load more world zones. Affects all LOD groups.", 0.25f, 5f); UseTreeLod = Bind(cfg, "Rendering", "UseTreeLod", p.UseTreeLod, "Adjust LOD transitions of loaded TreeBase trees independently. Does not change distant-world vegetation or load distance."); TreeLodMultiplier = Bind(cfg, "Rendering", "TreeLodMultiplier", p.TreeLodMultiplier, "Multiplier on tree LOD distances, in addition to the global bias. 1 preserves authored transitions. Higher values keep detailed trees longer and cost more GPU work.", 0.25f, 4f); } } internal class PostFxSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<AaMethod> Antialiasing; public readonly ConfigEntry<bool> CasSharpen; public readonly ConfigEntry<float> CasSharpness; public readonly ConfigEntry<bool> UseTaaSharpen; public readonly ConfigEntry<float> TaaSharpen; public readonly ConfigEntry<bool> UseTaaMotionBlending; public readonly ConfigEntry<float> TaaMotionBlending; public readonly ConfigEntry<bool> UseTaaSpread; public readonly ConfigEntry<float> TaaSpread; public readonly ConfigEntry<EffectOverride> Vignette; public readonly ConfigEntry<float> VignetteIntensity; public readonly ConfigEntry<float> VignetteSmoothness; public readonly ConfigEntry<float> VignetteRoundness; public readonly ConfigEntry<bool> DisableSsr; public readonly ConfigEntry<bool> AmbientOcclusion; public readonly ConfigEntry<bool> AoEvenFar; public readonly ConfigEntry<bool> UseAoIntensity; public readonly ConfigEntry<float> AoIntensity; public readonly ConfigEntry<bool> UseAoRadius; public readonly ConfigEntry<float> AoRadius; public readonly ConfigEntry<bool> UseAoForwardCompatibility; public readonly ConfigEntry<bool> AoForwardCompatibility; public readonly ConfigEntry<bool> UseAoIntensityFar; public readonly ConfigEntry<float> AoIntensityFar; public readonly ConfigEntry<bool> UseAoFarDistance; public readonly ConfigEntry<float> AoFarDistance; public readonly ConfigEntry<bool> UseAoSampleCount; public readonly ConfigEntry<SampleCount> AoSampleCount; public readonly ConfigEntry<bool> UseAoDownsampling; public readonly ConfigEntry<bool> AoDownsampling; public readonly ConfigEntry<bool> UseAoHighPrecision; public readonly ConfigEntry<bool> AoHighPrecision; public readonly ConfigEntry<bool> UseAoAmbientOnly; public readonly ConfigEntry<bool> AoAmbientOnly; public readonly ConfigEntry<bool> SsrEnabled; public readonly ConfigEntry<float> SsrIntensity; public readonly ConfigEntry<SSRResolution> SsrResolution; public readonly ConfigEntry<int> SsrIterationCount; public readonly ConfigEntry<float> SsrMaxDistance; public PostFxSection(ConfigFile cfg, PresetValues p) { //IL_0413: Unknown result type (might be due to invalid IL or missing references) //IL_0546: Unknown result type (might be due to invalid IL or missing references) UseTaaSpread = Bind(cfg, "PostFx", "UseTaaSpread", p.UseTaaSpread, "Override the spread of TAA jitter samples. Only applies when Antialiasing is Taa."); TaaSpread = Bind(cfg, "PostFx", "TaaSpread", p.TaaSpread, "TAA jitter diameter in pixels. Lower is sharper but shimmers more; higher is steadier but softer.", 0.1f, 1f); Vignette = Bind(cfg, "PostFx", "Vignette", p.Vignette, "Vanilla leaves the effect alone; Off explicitly disables it; On uses the controls below."); VignetteIntensity = Bind(cfg, "PostFx", "VignetteIntensity", p.VignetteIntensity, "Strength of the vignette when Vignette is On.", 0f, 1f); VignetteSmoothness = Bind(cfg, "PostFx", "VignetteSmoothness", p.VignetteSmoothness, "Softness of the vignette edge.", 0.01f, 1f); VignetteRoundness = Bind(cfg, "PostFx", "VignetteRoundness", p.VignetteRoundness, "Vignette shape, from square-like to round.", 0f, 1f); DisableSsr = Bind(cfg, "PostFx", "DisableSsr", p.DisableSsr, "Explicitly disable screen-space reflections, including ones enabled in the original profile. Takes precedence over SsrEnabled. Environment reflections have a separate switch under Reflections."); Enabled = Bind(cfg, "PostFx", "Enabled", def: true, "Whether the mod edits the post-processing profile at all. Off gives the game back its untouched profile, which is the cleanest way to tell a post effect apart from a lighting change."); Antialiasing = Bind(cfg, "PostFx", "Antialiasing", p.AaMode, "Vanilla, Taa or Fxaa. Vanilla means the mod does not touch antialiasing. TAA resolves edges by blending several jittered frames, so it is soft in motion and hits alpha-tested grass and leaves hardest — try Vanilla here first if foliage looks washed out or oddly lit while moving."); CasSharpen = Bind(cfg, "PostFx", "CasSharpen", def: false, "Contrast-adaptive sharpening (AMD FidelityFX CAS) of the final image. Crisps edges without turning details smaller than a pixel into hard specks far off; while it is on, TAA's own sharpen is not used. Grass and foliage are left unsharpened (sharpening them makes them ripple). Off by default: the game's own TAA is the steadiest on grass."); CasSharpness = Bind(cfg, "PostFx", "CasSharpness", p.CasSharpness, "Strength of the contrast-adaptive sharpening: 0 a little, 1 the most.", 0f, 1f); UseTaaSharpen = Bind(cfg, "PostFx", "UseTaaSharpen", p.UseTaaSharpen, "Off leaves TAA's own sharpen value. Only matters when Antialiasing is Taa."); TaaSharpen = Bind(cfg, "PostFx", "TaaSharpen", p.TaaSharpen, "Counter-sharpening applied by TAA, which is blurry by construction.", 0f, 3f); UseTaaMotionBlending = Bind(cfg, "PostFx", "UseTaaMotionBlending", p.UseTaaMotionBlending, "Off leaves TAA's own motion blending. Only matters when Antialiasing is Taa."); TaaMotionBlending = Bind(cfg, "PostFx", "TaaMotionBlending", p.TaaMotionBlending, "How much of the previous frame TAA keeps while the camera moves. Lower is crisper in motion and shimmers more; higher smears, which is what reads as a haze that clears when you stand still.", 0f, 0.99f); AmbientOcclusion = Bind(cfg, "PostFx", "AmbientOcclusion", p.Ao, "Configure and enable the game's ambient occlusion. Defaults use Medium samples with downsampling. Off returns control to the game's SSAO setting."); AoEvenFar = Bind(cfg, "PostFx", "AoEvenFar", p.AoEvenFar, "Shade the distance as much as the foreground. The game's profile darkens everything past AoFarDistance with its own far intensity, many times the near one, so distant land reads darker than it is. On: the far intensity follows the near one. Has no effect while GTAO is on (it replaces this occlusion)."); UseAoIntensity = Bind(cfg, "PostFx", "UseAoIntensity", p.UseAoIntensity, "Off leaves the profile's own intensity."); AoIntensity = Bind(cfg, "PostFx", "AoIntensity", p.AoIntensity, "Strength of ambient occlusion. Try 0.3 if the picture is hazy but AO is worth keeping.", 0f, 4f); UseAoRadius = Bind(cfg, "PostFx", "UseAoRadius", p.UseAoRadius, "Off — the default — leaves the radius the game's own profile was authored with. This is the switch behind \"AO makes the grass look overlit\": occlusion samples in world units, so a wide radius throws them a metre out into open air around a blade of grass, finds nothing to occlude with, and loses the contact shading at the base that gives a grass patch its depth. The ground keeps being shaded, the plants stop, and the difference reads as the plants being too bright."); AoRadius = Bind(cfg, "PostFx", "AoRadius", p.AoRadius, "How far ambient occlusion reaches, in world units, when the switch above is on. Valheim's own profile uses 0.15, and 0.3 here is the stack's default: both shade tight creases and contact points, which is what foliage needs. Around 1.0 it shades whole corners, which flatters buildings and flattens plants. Past that it stops looking like shadow and starts looking like dirt on the lens.", 0.05f, 4f); UseAoForwardCompatibility = Bind(cfg, "PostFx", "UseAoForwardCompatibility", p.UseAoForwardCompatibility, "Off leaves the profile's own setting, which in this stack defaults to reading the G-buffer."); AoForwardCompatibility = Bind(cfg, "PostFx", "AoForwardCompatibility", p.AoForwardCompatibility, "Compute occlusion from the depth buffer instead of the G-buffer. This is the setting behind \"AO makes the grass look overlit\": the G-buffer holds only what the deferred path drew, so anything rendered in the forward pass gets no usable normals there — grass and foliage come out unoccluded while the ground around them darkens, and that contrast reads as the plants being lit too brightly. On, occlusion covers the whole image. Costs a little more, and disables AmbientOnly."); UseAoIntensityFar = Bind(cfg, "PostFx", "UseAoIntensityFar", p.UseAoIntensityFar, "Off leaves the profile's own far intensity — 1.5 in Valheim's, half again the near value, which is a quiet reason distance looks darker than the number above suggests."); AoIntensityFar = Bind(cfg, "PostFx", "AoIntensityFar", p.AoIntensityFar, "Ambient occlusion strength past FarDistance. Set it equal to AoIntensity for occlusion that does not change with distance.", 0f, 4f); UseAoFarDistance = Bind(cfg, "PostFx", "UseAoFarDistance", p.UseAoFarDistance, "Off leaves the profile's own far distance. The game itself writes 100 or 150 m here depending on its SSAO setting."); AoFarDistance = Bind(cfg, "PostFx", "AoFarDistance", p.AoFarDistance, "Where ambient occlusion switches from AoIntensity to AoIntensityFar, in metres.", 10f, 500f); UseAoSampleCount = Bind(cfg, "PostFx", "UseAoSampleCount", p.UseAoSampleCount, "Off leaves the sample count the game chose (Low or Medium by its SSAO setting)."); AoSampleCount = Bind<SampleCount>(cfg, "PostFx", "AoSampleCount", p.AoSampleCount, "Occlusion samples per pixel: Lowest 3, Low 6, Medium 10, High 16. Too few is what makes ambient occlusion look like grain rather than shading."); UseAoDownsampling = Bind(cfg, "PostFx", "UseAoDownsampling", p.UseAoDownsampling, "Off leaves the game's own choice, which halves the resolution on its lower SSAO step."); AoDownsampling = Bind(cfg, "PostFx", "AoDownsampling", p.AoDownsampling, "Halve the resolution of the occlusion pass. Cheaper, and blurs occlusion across edges — which on grass means a dark halo where there should be a shaded blade."); UseAoHighPrecision = Bind(cfg, "PostFx", "UseAoHighPrecision", p.UseAoHighPrecision, "Off leaves the profile's own setting."); AoHighPrecision = Bind(cfg, "PostFx", "AoHighPrecision", p.AoHighPrecision, "Sample the full-precision depth texture instead of packed depth+normals when forward compatibility is on. This stack still requests a depth+normals texture, so this is not a guarantee of a cheaper pass."); UseAoAmbientOnly = Bind(cfg, "PostFx", "UseAoAmbientOnly", p.UseAoAmbientOnly, "Off leaves the profile's own setting."); AoAmbientOnly = Bind(cfg, "PostFx", "AoAmbientOnly", p.AoAmbientOnly, "Apply occlusion only to ambient light rather than to the finished image. Physically the more correct of the two, and the one that misses forward-rendered objects hardest — it needs the G-buffer, so it does nothing while AoForwardCompatibility is on."); SsrEnabled = Bind(cfg, "PostFx", "SsrEnabled", p.Ssr, "Screen space reflections, which the game never enables. Off by default: they are the most expensive effect here (about 4 ms of GPU time at 1440p) and barely visible. Water is transparent and never receives them, and most of Valheim's surfaces are too rough to show a sharp reflection — what remains is metal, slime and the like. One player measured 10 FPS lost and a stutter every few seconds with them on."); SsrIntensity = Bind(cfg, "PostFx", "SsrIntensity", p.SsrIntensity, "Multiplier on how much the screen space reflection contributes. 1 is the stack's own default.", 0f, 2f); SsrResolution = Bind<SSRResolution>(cfg, "PostFx", "SsrResolution", p.SsrResolution, "High traces at full screen resolution; Low traces at half resolution. Low is faster but may lose thin reflections and add edge artifacts."); SsrIterationCount = Bind(cfg, "PostFx", "SsrIterationCount", p.SsrIterationCount, "Maximum ray steps per screen-space reflection. Lower values reduce tracing work but can miss distant or off-angle reflections. The previous fixed value was 256.", 16, 1024); SsrMaxDistance = Bind(cfg, "PostFx", "SsrMaxDistance", p.SsrMaxDistance, "Maximum SSR ray distance in world units. The previous fixed value was 100 m; shorter rays can miss distant reflections.", 0.1f, 300f); } } internal class DofSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<float> MaxBlurSize; public readonly ConfigEntry<float> FocalSize; public readonly ConfigEntry<float> Aperture; public readonly ConfigEntry<bool> HighResolution; public readonly ConfigEntry<BlurSampleCount> BlurSampleCount; public DofSection(ConfigFile cfg, PresetValues p) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) Enabled = Bind(cfg, "DoF", "Enabled", p.DofEnabled, "Apply the values below to Valheim's world camera. The game's own Depth of Field graphics switch still controls whether the effect runs. Off restores the camera's original values."); MaxBlurSize = Bind(cfg, "DoF", "MaxBlurSize", p.DofMaxBlurSize, "Maximum blur radius. The measured world camera used 1.1; lower values make out-of-focus objects less blurry.", 0.1f, 3f); FocalSize = Bind(cfg, "DoF", "FocalSize", p.DofFocalSize, "Width of the in-focus region. The measured world camera used 0.97.", 0f, 2f); Aperture = Bind(cfg, "DoF", "Aperture", p.DofAperture, "Transition from focus to blur. The measured world camera used 0.68. Keep this below 1 to avoid the image effect's singularity.", 0f, 0.95f); HighResolution = Bind(cfg, "DoF", "HighResolution", p.DofHighResolution, "Render the blur at the image effect's higher resolution. Disable to trade blur quality for GPU time."); BlurSampleCount = Bind<BlurSampleCount>(cfg, "DoF", "BlurSampleCount", p.DofBlurSampleCount, "Low, Medium or High disc-blur sample count. Valheim's measured world camera used Medium; High matches the author's current configuration and costs more GPU time."); } } internal class VegetationSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<bool> UseDistance; public readonly ConfigEntry<float> Distance; public readonly ConfigEntry<bool> UseAmount; public readonly ConfigEntry<float> Amount; public VegetationSection(ConfigFile cfg, PresetValues p) { Enabled = Bind(cfg, "Vegetation", "Enabled", def: true, "Grass and small props. This is near-field clutter only, not draw distance — that belongs to DistantValheim."); UseDistance = Bind(cfg, "Vegetation", "UseDistance", p.UseClutterDistance, "Off leaves the game's own clutter radius (about 40 m: fewer than 200 grass renderers at a base against some 2400 at 200 m). On applies only while SteadyFrame draws the grass on the GPU."); Distance = Bind(cfg, "Vegetation", "Distance", p.ClutterDistance, "How far grass and small props are drawn, in metres. Every 8 m patch is its own draw each frame, so the cost grows with the area: at a large base (2560x1440, RTX 4070 SUPER, 12 threads) 200 m cost 3-4.7 ms of main thread and about 1 ms of GPU more than the game's 40 m, and 1 ms main plus 0.5 ms GPU more than 100 m. With this on, the kinds that stop at 100 m of their own run to the radius too: at 200 m on open Plains a further 1.6 ms a frame, or 0.3 ms with SteadyFrame drawing the grass on the GPU. Only while SteadyFrame draws the grass on the GPU: without it the game's own radius stays, the cost being the main thread's. Raise it while watching frame time.", 20f, 400f); UseAmount = Bind(cfg, "Vegetation", "UseAmount", p.UseClutterAmount, "Off leaves the game's own density. On, the multiplier below applies — and unlike most of this mod it costs CPU, not GPU."); Amount = Bind(cfg, "Vegetation", "Amount", p.ClutterAmount, "Grass and undergrowth density multiplier. 1 is vanilla.", 0.1f, 5f); } } internal class ReflectionsSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<bool> UseResolution; public readonly ConfigEntry<int> Resolution; public readonly ConfigEntry<bool> ForceRealtimeProbes; public readonly ConfigEntry<bool> DisableEnvironmentReflections; public readonly ConfigEntry<float> RefreshSeconds; public readonly ConfigEntry<bool> SmoothRefresh; public ReflectionsSection(ConfigFile cfg, PresetValues p) { DisableEnvironmentReflections = Bind(cfg, "Reflections", "DisableEnvironmentReflections", p.DisableEnvironmentReflections, "Suppress environment reflection probes and the sky reflection fallback, and pause Valheim's reflection refreshes. Does not disable direct-light specular highlights or SSR. Yields to WeatherOverhaul when it owns reflection probes."); Enabled = Bind(cfg, "Reflections", "Enabled", def: true, "Environment reflection probes. This is the one large system the game never configures, so unlike anisotropy or the light limit there is real headroom here."); UseResolution = Bind(cfg, "Reflections", "UseResolution", p.UseReflectionResolution, "Off leaves every probe at the resolution it was authored with."); Resolution = Bind(cfg, "Reflections", "Resolution", p.ReflectionResolution, "Cubemap resolution of reflection probes; powers of two only. A cubemap is six scene renders, so cost grows with the square of this — it lands on the GPU, not the main thread.", 16, 2048); RefreshSeconds = Bind(cfg, "Reflections", "RefreshSeconds", p.ReflectionRefreshSeconds, "How often the reflections are made again, in seconds, and how long they blend to the new one. The game: every 3 s with a 3 s blend, so reflections trailed up to 6 s behind. Lower follows the scene sooner and costs a few more busy frames (each refresh renders the scene six times, over several frames).", 0.25f, 3f); SmoothRefresh = Bind(cfg, "Reflections", "SmoothRefresh", p.ReflectionSmoothRefresh, "Fade each new reflection in over the refresh time. The game's own blend sweeps out from the player and switches every spot it passes at once, so water and metal visibly jump at each refresh. A fraction of a millisecond on the GPU while fading."); ForceRealtimeProbes = Bind(cfg, "Reflections", "ForceRealtimeProbes", p.ForceRealtimeProbes, "Forces QualitySettings.realtimeReflectionProbes on. Valheim already has it on, so this changes nothing there and exists as insurance against another mod or a quality preset turning it off — in which case the probe resolution below would be spent on cubemaps that never refresh."); } } internal class MaterialsSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<float> NormalIntensity; public MaterialsSection(ConfigFile cfg, PresetValues p) { Enabled = Bind(cfg, "Materials", "NormalBoost", p.NormalBoostEnabled, "Strengthen normal maps across every material that has one — buildings, rocks, creatures, props, foliage. The detail is already authored and already in memory: the log wall carries a 4096x4096 normal map applied at intensity 1.0. This only reads it harder, so it costs no geometry and no draw calls. It does change how every surface catches light, which makes it worth toggling when the lighting itself looks wrong."); NormalIntensity = Bind(cfg, "Materials", "NormalIntensity", p.NormalIntensity, "Multiplier on each material's own normal intensity, not a flat replacement — a surface an artist deliberately kept subtle stays subtle. 1 is vanilla. Past about 2 the lighting starts to look embossed rather than carved.", 0.1f, 4f); } } internal class WaterSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<bool> UseTessellation; public readonly ConfigEntry<float> Tessellation; public readonly ConfigEntry<bool> UseRefraction; public readonly ConfigEntry<float> RefractionScale; public readonly ConfigEntry<float> RefractionMax; public readonly ConfigEntry<bool> UseNormalPower; public readonly ConfigEntry<float> NormalPower; public readonly ConfigEntry<bool> UseLodDistance; public readonly ConfigEntry<float> LodDistance; public readonly ConfigEntry<bool> UseSmoothness; public readonly ConfigEntry<float> Smoothness; public readonly ConfigEntry<bool> UseMetallic; public readonly ConfigEntry<float> Metallic; public WaterSection(ConfigFile cfg, PresetValues p) { Enabled = Bind(cfg, "Water", "Enabled", def: true, "Turn up the parts of the game's own water shader that ship almost switched off. Nothing here is invented: every value is a property Custom/Water declares, moved inside the range its author set."); UseTessellation = Bind(cfg, "Water", "UseTessellation", p.UseWaterTessellation, "Off leaves the shader's own tessellation (4 of a possible 32)."); Tessellation = Bind(cfg, "Water", "Tessellation", p.WaterTessellation, "Real geometry for waves instead of a flat plane shaded to look bumpy. This is the single biggest change to how water reads, and it is paid in GPU time. Drop it if the shoreline starts costing frames.", 1f, 32f); UseRefraction = Bind(cfg, "Water", "UseRefraction", p.UseWaterRefraction, "Off leaves the shader's own refraction, which is capped at 0.01 — close to none at all."); RefractionScale = Bind(cfg, "Water", "RefractionScale", p.WaterRefractionScale, "How strongly the surface normal bends the view through it, before the cap below.", 0f, 2f); RefractionMax = Bind(cfg, "Water", "RefractionMax", p.WaterRefractionMax, "Cap on that bend. Raise gently: past about 0.1 the bottom starts to wobble like jelly.", 0f, 0.5f); UseNormalPower = Bind(cfg, "Water", "UseNormalPower", p.UseWaterNormalPower, "Off leaves the shader's own ripple strength (0.5 of 5)."); NormalPower = Bind(cfg, "Water", "NormalPower", p.WaterNormalPower, "Strength of the fine ripple on the surface. With tessellation giving the waves real geometry, strong normals on top read as noise rather than ripple.", 0f, 5f); UseLodDistance = Bind(cfg, "Water", "UseLodDistance", p.UseWaterLodDistance, "Off leaves the shader's own LOD distance (120 m)."); LodDistance = Bind(cfg, "Water", "LodDistance", p.WaterLodDistance, "Metres from the camera where the waves hand over to the game's flat far sea; the far sea is set to the same distance, so the two do not overlap (left at 120 m it covered the troughs and the crests stood out as a dark band along the horizon). At most 350: the zones that carry the waves reach about that far. Costs GPU time for the extra wave surface drawn.", 50f, 350f); UseSmoothness = Bind(cfg, "Water", "UseSmoothness", p.UseWaterSmoothness, "Off leaves the shader's own smoothness (0.94)."); Smoothness = Bind(cfg, "Water", "Smoothness", p.WaterSmoothness, "How mirror-like the surface is. The last stretch towards 1 is where a reflection stops being a soft sheen and starts showing the shoreline back. Costs nothing, but works with reflection probe resolution: a sharp mirror of a blurry cubemap is still blurry.", 0f, 1f); UseMetallic = Bind(cfg, "Water", "UseMetallic", p.UseWaterMetallic, "Off leaves the shader's own value (0)."); Metallic = Bind(cfg, "Water", "Metallic", p.WaterMetallic, "Metallic tints reflections with the surface colour, and at 0.8 that reads as a real body of water rather than a sheet of glass — chosen by eye, against the initial assumption that it should be left alone.", 0f, 1f); } } internal class ContactShadowsSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<float> Length; public readonly ConfigEntry<float> Distance; public readonly ConfigEntry<float> Strength; public readonly ConfigEntry<float> Thickness; public ContactShadowsSection(ConfigFile cfg, PresetValues p) { Enabled = Bind(cfg, "ContactShadows", "Enabled", def: true, "Short sun shadows the shadow map is too coarse for: stones on the ground, plank edges, the foot of a post, grass at its roots. Traced on screen and written into the game's own sun shadows, so only direct sunlight is darkened. A shader of the mod's own. Needs the game's shadows on."); Length = Bind(cfg, "ContactShadows", "Length", p.ContactShadowLength, "Metres each pixel looks towards the sun. Longer catches bigger things but starts drawing shadows the depth buffer can only guess.", 0.05f, 2f); Distance = Bind(cfg, "ContactShadows", "Distance", p.ContactShadowDistance, "Metres from the camera where they have faded out.", 5f, 300f); Strength = Bind(cfg, "ContactShadows", "Strength", p.ContactShadowStrength, "1 is a full shadow; lower lets some sunlight through.", 0f, 1f); Thickness = Bind(cfg, "ContactShadows", "Thickness", p.ContactShadowThickness, "How deep, in metres, a surface on screen is assumed to be. The screen has no depth behind what it shows: too thick and a post shadows the ground well behind it, too thin and solid things let the light through.", 0.02f, 2f); } } internal class CausticsSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<float> Strength; public readonly ConfigEntry<float> Size; public readonly ConfigEntry<float> Speed; public readonly ConfigEntry<float> MaxDepth; public readonly ConfigEntry<float> ColourSplit; public CausticsSection(ConfigFile cfg, PresetValues p) { Enabled = Bind(cfg, "Caustics", "Enabled", def: true, "Rippling light on the sea floor and on whatever stands in shallow water, as the waves focus the sun. Follows the game's sunlight and its shadows (docks, hulls, trees). Works with any water."); Strength = Bind(cfg, "Caustics", "Strength", p.CausticsStrength, "How much of the sunlight on the floor the ripples gather into lines (taken from between them: the floor gets as much light on average). 1 is the full computed pattern, with nearly dark cells; 0.6 leaves the cells 40% of the sunlight, as the scattering of real water does.", 0f, 1.5f); Size = Bind(cfg, "Caustics", "Size", p.CausticsSize, "Metres the ripples repeat over; the light's cells are a tenth to a twentieth of it. Larger ripples also gather the light deeper down.", 1f, 16f); Speed = Bind(cfg, "Caustics", "Speed", p.CausticsSpeed, "How fast the ripples move, 1 being as fast as real water waves of their size. 0 freezes them.", 0f, 4f); MaxDepth = Bind(cfg, "Caustics", "MaxDepth", p.CausticsMaxDepth, "Metres under the surface where the light has faded out.", 1f, 20f); ColourSplit = Bind(cfg, "Caustics", "ColourSplit", p.CausticsColourSplit, "Rainbow fringes along the bright lines, wider the deeper the light has gone. 0 keeps them white.", 0f, 3f); } } internal class CloudShadowsSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<float> Strength; public CloudShadowsSection(ConfigFile cfg, PresetValues p) { Enabled = Bind(cfg, "CloudShadows", "Enabled", def: false, "The game's clouds cast moving shadows on the land, where they are on the sky. They fade out in rain, when the sky is one grey deck. They reach as far as the game's sun shadows (about 300 m) and need them on. Stands down while WeatherOverhaul draws the clouds (it casts its own clouds' shadows)."); Strength = Bind(cfg, "CloudShadows", "Strength", p.CloudShadowStrength, "How much sunlight a thick cloud takes away. 1 leaves none under it.", 0f, 1f); } } internal class GtaoSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<float> Radius; public readonly ConfigEntry<float> Strength; public readonly ConfigEntry<float> Thickness; public readonly ConfigEntry<int> Quality; public GtaoSection(ConfigFile cfg, PresetValues p) { Enabled = Bind(cfg, "Gtao", "Enabled", def: true, "Ambient occlusion that darkens only the sky light, not the sun or torches: corners of rooms, under eaves, between roots, without the grey haze the post-processing occlusion gives the whole landscape. Replaces that occlusion (PostFx.AmbientOcclusion and the game's SSAO setting are overridden off while this is on). A shader of the mod's own."); Radius = Bind(cfg, "Gtao", "Radius", p.GtaoRadius, "Metres around each point that can shade it.", 0.2f, 5f); Strength = Bind(cfg, "Gtao", "Strength", p.GtaoStrength, "1 is the full measured occlusion; lower keeps some sky light in corners.", 0f, 1f); Thickness = Bind(cfg, "Gtao", "Thickness", p.GtaoThickness, "How deep, in metres, a surface on screen is assumed to be. Thin things (grass, posts, branches) hide only what lies this close behind them, so they cast no dark halo on everything beyond.", 0.05f, 3f); Quality = Bind(cfg, "Gtao", "Quality", p.GtaoQuality, "Directions sampled per pixel. Each one costs about as much as the first; the noise left over is blurred away.", 1, 4); } } internal class IndirectLightSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<float> Intensity; public readonly ConfigEntry<float> Radius; public readonly ConfigEntry<float> Thickness; public readonly ConfigEntry<int> Quality; public IndirectLightSection(ConfigFile cfg, PresetValues p) { Enabled = Bind(cfg, "IndirectLight", "Enabled", def: false, "Light bouncing between surfaces on screen: firelight off the walls onto the floor, sunlit ground lighting the underside of a roof, colour from grass on the trees beside it. The game lights everything with direct light plus one flat sky colour. Screen space: what is off screen or hidden does not bounce. The most expensive of the screen-space effects; a shader of the mod's own, off until switched on."); Intensity = Bind(cfg, "IndirectLight", "Intensity", p.IndirectIntensity, "1 is one physically scaled bounce. Above it reads stronger than real light would.", 0f, 3f); Radius = Bind(cfg, "IndirectLight", "Radius", p.IndirectRadius, "Metres light is gathered from. Wider reaches across rooms at the same cost, but gathers coarser.", 0.5f, 10f); Thickness = Bind(cfg, "IndirectLight", "Thickness", p.IndirectThickness, "How deep, in metres, a surface on screen is assumed to be, as for Gtao.Thickness.", 0.05f, 3f); Quality = Bind(cfg, "IndirectLight", "Quality", p.IndirectQuality, "Directions sampled per pixel. More is smoother and costs proportionally more.", 1, 4); } } internal class ParticlesSection { public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<bool> UseTranslucency; public readonly ConfigEntry<float> Translucency; public readonly ConfigEntry<bool> UseSoftness; public readonly ConfigEntry<float> Softness; public readonly ConfigEntry<bool> UseTessellation; public readonly ConfigEntry<float> Tessellation; public readonly ConfigEntry<bool> UseLightNormal; public readonly ConfigEntry<float> LightNormal; public ParticlesSection(ConfigFile cfg, PresetValues p) { Enabled = Bind(cfg, "Particles", "Enabled", def: true, "Improve how smoke, mist and other particles are lit. Found by scanning shaders for values parked at the bottom of their own range: smoke ships with translucency at 0.19 out of 1.0."); UseTranslucency = Bind(cfg, "Particles", "UseTranslucency", p.UseParticleTranslucency, "Off leaves each effect's own translucency and wrapped diffuse."); Translucency = Bind(cfg, "Particles", "Translucency", p.ParticleTranslucency, "Multiplier on how much light passes through a particle. This is what makes smoke above a hearth glow from the fire inside it instead of hanging there as a grey blob. A multiplier, not a fixed value, so effects authored subtle stay subtle. 1 is vanilla.", 0f, 5f); UseSoftness = Bind(cfg, "Particles", "UseSoftness", p.UseParticleSoftness, "Off leaves each effect's own soft-particle factor."); Softness = Bind(cfg, "Particles", "Softness", p.ParticleSoftness, "Multiplier on the soft-particle factor, which fades a particle where it intersects solid geometry. Vanilla smoke cuts a hard line into the ground; the shader allows up to 3.", 0f, 3f); UseTessellation = Bind(cfg, "Particles", "UseTessellation", p.UseParticleTessellation, "Off leaves the tessellated mist shader alone."); Tessellation = Bind(cfg, "Particles", "Tessellation", p.ParticleTessellation, "Subdivisions for the tessellated mist shader used by Mistlands fog. The shader declares 1..13 and ships at 3.", 1f, 13f); UseLightNormal = Bind(cfg, "Particles", "UseLightNormal", p.UseParticleLightNormal, "Off leaves the shader's own value, which for snow and rain is exactly 0."); LightNormal = Bind(cfg, "Particles", "LightNormalFactor", p.ParticleLightNormal, "How much a particle's facing affects how it is lit. Snow and rain ship at 0 — a flake is lit identically whichever way it is turned, which is why falling snow reads as flat white specks. At 0.5 flakes facing a fire or the moon catch the light and those turned away stay dark. Set rather than multiplied, because vanilla is zero and scaling zero changes nothing.", 0f, 1f); } } public readonly ConfigEntry<bool> Enabled; public readonly ConfigEntry<KeyboardShortcut> PresetKey; public readonly ShadowsSection Shadows; public readonly SurfacesSection Surfaces; public readonly PostFxSection PostFx; public readonly DofSection DoF; public readonly VegetationSection Vegetation; public readonly ReflectionsSection Reflections; public readonly MaterialsSection Materials; public readonly WaterSection Water; public readonly ContactShadowsSection ContactShadows; public readonly CausticsSection Caustics; public readonly CloudShadowsSection CloudShadows; public readonly GtaoSection Gtao; public readonly IndirectLightSection IndirectLight; public readonly ParticlesSection Particles; public readonly RenderingSection Rendering; internal const int DefaultsVersion = 3; public VisualsConfig(ConfigFile cfg) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) PresetValues physical = Presets.Physical; Enabled = Bind(cfg, "General", "Enabled", def: true, "Master switch. Off restores every value the mod changed and stands down."); PresetKey = cfg.BindListed<KeyboardShortcut>("Keys", "PresetKey", new KeyboardShortcut((KeyCode)275, Array.Empty<KeyCode>()), "Compare: switches the mod's look off and back on for this session, to judge what it changes (the config is not touched). Taken elsewhere and deliberately avoided: F5 console, F6/F9/F11 perf probe, F8/F10 ValheimAcoustics, F7 DistantValheim diagnostics, PageDown Badgers, arrows and Insert/End/Delete DistantValheim."); Shadows = new ShadowsSection(cfg, physical); Surfaces = new SurfacesSectio