using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using DelaunatorSharp; using HarmonyLib; using Microsoft.CodeAnalysis; using Riverheim.Configuration; using Riverheim.Configuration.Extensions; using Riverheim.Pipeline; using Riverheim.Pipeline.Extensions; using Riverheim.Rendering; using Riverheim.Rendering.Biomes; using Riverheim.Rendering.Rivers; using Riverheim.Rendering.Splats; using Riverheim.Util; using Riverheim.Util.Debug; using Riverheim.Util.Geometry; using Riverheim.Util.Grids; using Riverheim.Util.Interpolation; using Riverheim.Util.Mathematics; using Riverheim.Util.Random; using Riverheim.Util.Random.Noise; using Riverheim.Util.Random.Noise.Primitives; using Riverheim.Util.Random.Primitives; using Riverheim.Util.Splines; using Riverheim.Util.Tilings; using Riverheim.Util.Tilings.Containers; using Riverheim.Util.Tilings.Internal; using Riverheim.Util.Tilings.Processing; using Riverheim.World; using Riverheim.World.Biomes; using Riverheim.World.Biomes.Competitive; using Riverheim.World.Height; using Riverheim.World.Height.Mountains; using Riverheim.World.Landmass; using Riverheim.World.Rivers; using Riverheim.World.Splats; using Riverheim.World.Tilings; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("assembly_utils")] [assembly: IgnoresAccessChecksTo("assembly_valheim")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("Riverheim")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Comprehensive terrain generator for Valheim")] [assembly: AssemblyFileVersion("0.15.3.0")] [assembly: AssemblyInformationalVersion("0.15.3+82a0f3fb356bdecf6c2e7743a616e1bd32179fec")] [assembly: AssemblyProduct("Riverheim")] [assembly: AssemblyTitle("Riverheim")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.15.3.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class ExtensionMarkerAttribute : Attribute { public ExtensionMarkerAttribute(string name) { } } } internal static class PluginInfo { public const string PLUGIN_GUID = "Riverheim"; public const string PLUGIN_NAME = "Riverheim"; public const string PLUGIN_VERSION = "0.15.3"; } namespace Riverheim.Plugin { public static class WorldExtensions { private class ExtensionData { public WorldSettings settings; } [SpecialName] public sealed class $7B5663A8641255449155D6EF66167945 { [SpecialName] public static class $45CD4584ACAF47103DDEBE68327735F9 { } [ExtensionMarker("$45CD4584ACAF47103DDEBE68327735F9")] private ExtensionData GetData() { throw null; } [ExtensionMarker("$45CD4584ACAF47103DDEBE68327735F9")] public WorldSettings GetSettings() { throw null; } [ExtensionMarker("$45CD4584ACAF47103DDEBE68327735F9")] public void SetSettings(WorldSettings settings) { throw null; } } private static readonly ConditionalWeakTable Storage = new ConditionalWeakTable(); private static WorldSettings GetDefaultSettings(World world) { if (world.m_name.StartsWith("RHEXP")) { return new WorldSettings("builtin.experimental", 0); } return ConfigManager.DefaultWorldSettings(); } private static ExtensionData GetData(this World world) { return Storage.GetValue(world, (World wld) => new ExtensionData { settings = GetDefaultSettings(wld) }); } public static WorldSettings GetSettings(this World world) { return world.GetData().settings; } public static void SetSettings(this World world, WorldSettings settings) { world.GetData().settings = settings; } } [BepInPlugin("dev.gurebu.riverheim.unstable", "Riverheim.Unstable", "0.15.3")] [BepInIncompatibility("dev.gurebu.riverheim")] [BepInIncompatibility("dev.gurebu.riverheim.stable")] [BepInIncompatibility("BetterContinents")] public class RiverheimPlugin : BaseUnityPlugin { public class ValheimWorldRenderer { [CompilerGenerated] private WorldRenderer P; [CompilerGenerated] private float P; private const double FOREST_FACTOR_MULTIPLIER = 2.15; private readonly ThreadLocal<(Rfloat2, IWorldGen.PointData)> cache; public ValheimWorldRenderer(WorldRenderer renderer, float heightOffset) { P = renderer; P = heightOffset; cache = new ThreadLocal<(Rfloat2, IWorldGen.PointData)>(); base..ctor(); } private static Biome ToValheim(Biome biome) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0051: 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_006b: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) switch (biome) { case Biome.Meadows: case Biome.Marsh: return (Biome)1; case Biome.Swamp: return (Biome)2; case Biome.Mountain: return (Biome)4; case Biome.Forest: return (Biome)8; case Biome.Plains: return (Biome)16; case Biome.Ashlands: case Biome.Lavaplain: return (Biome)32; case Biome.DeepNorth: return (Biome)64; case Biome.Ocean: return (Biome)256; case Biome.Mistlands: case Biome.Mistglade: return (Biome)512; default: return (Biome)0; } } public float GetHeight(float wx, float wy, out Color mask) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) Rfloat2 rfloat = new Rfloat2(wx, wy); if (!cache.IsValueCreated || cache.Value.Item1 != rfloat) { cache.Value = (rfloat, P.RenderPoint(rfloat)); } IWorldGen.PointData item = cache.Value.Item2; mask = new Color((float)item.dirt, 0f, (float)item.pave, (float)Math.Max(item.lava, item.fertility)); return P + (float)item.height; } public Biome GetBiome(float wx, float wy) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) return ToValheim(P.GetBiome(new Rfloat2(wx, wy))); } public float GetForestFactor(float wx, float wy) { return (float)(2.15 * (1.0 - P.GetForestation(new Rfloat2(wx, wy)))); } } [HarmonyPatch(typeof(WorldGenerator))] private static class WorldGeneratorPatch { private static GeneratorPipeline BuildPipeline() { string[] resultResourceIds = new string[1] { "Rendering/WorldRenderer" }; GeneratorPipeline pipeline = new GeneratorPipeline.Builder(resultResourceIds).SetParallel(value: true).SetClearUnneededCache(value: true).Build(); pipeline.PipelineCompleted += delegate(GeneratorPipeline.PipelineCompletionEvent evt) { Logger.LogInfo($"World generation completed in {evt.duration.TotalMilliseconds:0.}ms"); }; pipeline.TaskCompleted += delegate(GeneratorPipeline.TaskCompletionEvent evt) { string text = ""; string text2 = string.Join(", ", evt.resources); string text3 = evt.duration.TotalMilliseconds.ToString("0."); Logger.LogInfo(evt.cached ? $"[{evt.progress} / {pipeline.Length}] {text2} retrieved from cache" : $"[{evt.progress} / {pipeline.Length}] {text2} computed in {text3}ms {text}"); }; return pipeline; } [HarmonyPrefix] [HarmonyPatch("Pregenerate")] private static bool Pregenerate(WorldGenerator __instance) { World world = __instance.m_world; if (world.m_menu) { return true; } if (world.m_seed == 0) { Logger.LogWarning("Seed is 0 for world '" + world.m_name + "', but not a menu world, must be an error"); Logger.LogWarning("Skipping world generation, treating as a vanilla world"); return true; } GeneratorPipeline generatorPipeline = BuildPipeline(); WorldSettings settings = world.GetSettings(); Logger.LogInfo($"Generating world '{world.m_name}'/{world.m_seed} with preset: '{settings.Preset}'..."); generatorPipeline.Compute(world.m_seed, ConfigManager.GetConfig(settings)); __instance.SetRenderer(new ValheimWorldRenderer(generatorPipeline.GetResult("Rendering/WorldRenderer"), 30f)); return false; } [HarmonyPrefix] [HarmonyPatch("GetBiomeHeight")] private static bool GetBiomeHeight(WorldGenerator __instance, Biome biome, float wx, float wy, out Color mask, ref float __result) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) mask = Color.black; ValheimWorldRenderer renderer = __instance.GetRenderer(); if (renderer == null) { return true; } __result = renderer.GetHeight(wx, wy, out mask); return false; } [HarmonyPrefix] [HarmonyPatch("GetHeight")] [HarmonyPatch(new Type[] { typeof(float), typeof(float) })] private static bool GetHeight(WorldGenerator __instance, float wx, float wy, ref float __result) { ValheimWorldRenderer renderer = __instance.GetRenderer(); if (renderer == null) { return true; } __result = renderer.GetHeight(wx, wy, out var _); return false; } [HarmonyPrefix] [HarmonyPatch("GetHeight")] [HarmonyPatch(/*Could not decode attribute arguments.*/)] private static bool GetHeightWithMask(WorldGenerator __instance, float wx, float wy, out Color mask, ref float __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) mask = Color.black; ValheimWorldRenderer renderer = __instance.GetRenderer(); if (renderer == null) { return true; } __result = renderer.GetHeight(wx, wy, out mask); return false; } [HarmonyPrefix] [HarmonyPatch("GetBiome", new Type[] { typeof(float), typeof(float), typeof(float), typeof(bool) })] private static bool GetBiome(WorldGenerator __instance, float wx, float wy, ref Biome __result) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected I4, but got Unknown ValheimWorldRenderer renderer = __instance.GetRenderer(); if (renderer == null) { return true; } __result = (Biome)(int)renderer.GetBiome(wx, wy); return false; } [HarmonyPrefix] [HarmonyPatch("GetForestFactor")] private static bool GetForestFactor(ref Vector3 pos, ref float __result) { ValheimWorldRenderer renderer = WorldGenerator.instance.GetRenderer(); if (renderer == null) { return true; } __result = renderer.GetForestFactor(pos.x, pos.z); return false; } [HarmonyPrefix] [HarmonyPatch("GetAshlandsHeight")] private static bool GetAshlandsHeight(WorldGenerator __instance, float wx, float wy, out Color mask, ref float __result) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) mask = Color.black; ValheimWorldRenderer renderer = __instance.GetRenderer(); if (renderer == null) { return true; } __result = renderer.GetHeight(wx, wy, out mask); return false; } } [HarmonyPatch(typeof(World))] private static class WorldSaveLoadPatch { private const int CUSTOM_WORLDGEN_MARKER = -1; private const string RIVERHEIM_WORLDGEN = "dev.gurebu.riverheim.unstable"; private static void Log(string message) { Logger.LogInfo(message); } private static void WriteHeader(ZPackage package) { package.Write(-1); package.Write("dev.gurebu.riverheim.unstable"); } private static bool ReadHeader(ZPackage package) { int num = package.ReadInt(); if (num != -1) { Log($"World doesn't have custom worldgen marker {-1}, instead got: {num}, skipping"); return false; } string text = package.ReadString(); if (text != "dev.gurebu.riverheim.unstable") { Log("Custom worldgen '" + text + "' does not match 'dev.gurebu.riverheim.unstable', skipping"); return false; } return true; } private static void WriteWorldSettings(ZPackage package, World world) { package.Write(world.GetSettings()); } private static bool ReadWorldSettings(ZPackage package, World world) { try { world.SetSettings(package.ReadWorldSettings()); } catch (WorldSettingSerialization.Error error) { Log("World settings malformed or unsupported: " + error.Message + ", skipping"); return false; } return true; } [HarmonyTranspiler] [HarmonyPatch("SaveWorldMetaData")] [HarmonyPatch(/*Could not decode attribute arguments.*/)] private static IEnumerable TranspileWorldSave(IEnumerable instructions) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown CodeInstruction ld_ZPackage; CodeInstruction ld_World; return CodeMatcherExtensions.AdvanceToAfterZPackageConstructor(new CodeMatcher(instructions, (ILGenerator)null), out ld_ZPackage).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { ld_ZPackage }).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstruction.Call(typeof(WorldSaveLoadPatch), "WriteHeader", (Type[])null, (Type[])null) }) .AdvanceToAfterWorldVersionWrite(out ld_World) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { ld_ZPackage }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { ld_World }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstruction.Call(typeof(WorldSaveLoadPatch), "WriteWorldSettings", (Type[])null, (Type[])null) }) .DebugLog() .InstructionEnumeration(); } [HarmonyTranspiler] [HarmonyPatch("LoadWorld")] private static IEnumerable TranspileWorldLoad(IEnumerable instructions, ILGenerator generator) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Expected O, but got Unknown //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Expected O, but got Unknown Label label; CodeInstruction ld_ZPackage; CodeInstruction ld_World; return CodeMatcherExtensions.MatchBadVersionWorldConstructor(new CodeMatcher(instructions, generator), out label).Start().AdvanceToAfterZPackageConstructor(out ld_ZPackage) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { ld_ZPackage }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstruction.Call(typeof(WorldSaveLoadPatch), "ReadHeader", (Type[])null, (Type[])null) }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Brfalse_S, (object)label) }) .AdvanceToAfterDefaultWorldConstructor(out ld_World) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { ld_ZPackage }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { ld_World }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstruction.Call(typeof(WorldSaveLoadPatch), "ReadWorldSettings", (Type[])null, (Type[])null) }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Brfalse_S, (object)label) }) .DebugLog() .InstructionEnumeration(); } } [HarmonyPatch(typeof(World))] private static class WorldServerLoadPatch { [HarmonyPrefix] [HarmonyPatch("GetCreateWorld")] private static void GetCreateWorld(ref World __result, string name, FileSource source) { //IL_001e: 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) SaveWithBackups val = default(SaveWithBackups); if (SaveSystem.TryGetSaveByName(name, (SaveDataType)0, ref val) && !val.IsDeleted) { __result = World.LoadWorld(val); if ((int)__result.m_dataError != 0) { throw new Exception($"Failed to load world with name \"{name}\", data error {__result.m_dataError}."); } } } } [HarmonyPatch(typeof(ZNet))] private static class ZNetPatch { private static readonly Version LocalVersion = new Version("0.15.3"); private static readonly ConditionalWeakTable PeerVersions = new ConditionalWeakTable(); private static bool VersionIsCompatible(Version remoteVersion) { if (LocalVersion.Major == remoteVersion.Major) { return LocalVersion.Minor == remoteVersion.Minor; } return false; } private static void RPC_AssertModVersion(ZRpc sender, string versionString) { Version version = new Version(versionString); PeerVersions.Add(sender, version); if (!VersionIsCompatible(version)) { Logger.LogWarning($"Riverheim plugin version mismatch! Local version: {LocalVersion}, remote: {version}"); sender.Invoke("Error", new object[1] { (object)(ConnectionStatus)3 }); } } [HarmonyPriority(700)] [HarmonyPrefix] [HarmonyPatch("OnNewConnection")] private static void OnNewConnection(ZNet __instance, ZNetPeer peer) { peer.m_rpc.Register("AssertRiverheimVersion", (Action)RPC_AssertModVersion); } [HarmonyPriority(700)] [HarmonyPrefix] [HarmonyPatch("RPC_ClientHandshake")] private static void RPC_ClientHandshake(ZNet __instance, ZRpc rpc) { rpc.Invoke("AssertRiverheimVersion", new object[1] { "0.15.3" }); } [HarmonyPriority(700)] [HarmonyPrefix] [HarmonyPatch("RPC_ServerHandshake")] private static void RPC_ServerHandshake(ZNet __instance, ZRpc rpc) { rpc.Invoke("AssertRiverheimVersion", new object[1] { "0.15.3" }); } [HarmonyPriority(700)] [HarmonyPrefix] [HarmonyPatch("SendPeerInfo")] private static bool SendPeerInfo(ZNet __instance, ZRpc rpc) { if (ZNet.instance.IsServer()) { return true; } if (PeerVersions.TryGetValue(rpc, out var value) && VersionIsCompatible(value)) { return true; } Logger.LogWarning($"Riverheim plugin version mismatch! Local version: {LocalVersion}, remote: {value}"); rpc.Invoke("Disconnect", Array.Empty()); return false; } [HarmonyPriority(700)] [HarmonyPrefix] [HarmonyPatch("RPC_PeerInfo")] private static bool RPC_PeerInfo(ZNet __instance, ZRpc rpc) { if (!ZNet.instance.IsServer()) { return true; } if (PeerVersions.TryGetValue(rpc, out var value) && VersionIsCompatible(value)) { return true; } Logger.LogWarning($"Riverheim plugin version mismatch! Local version: {LocalVersion}, remote: {value}"); rpc.Invoke("Error", new object[1] { (object)(ConnectionStatus)3 }); return false; } } private const string GUID_BETA = "dev.gurebu.riverheim"; private const string GUID_STABLE = "dev.gurebu.riverheim.stable"; private const string GUID_UNSTABLE = "dev.gurebu.riverheim.unstable"; private const string GUID = "dev.gurebu.riverheim.unstable"; private const string GUID_INCOMPATIBLE = "dev.gurebu.riverheim.stable"; private const string PLUGIN_NAME = "Riverheim.Unstable"; public void Awake() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) new Harmony("dev.gurebu.riverheim.unstable").PatchAll(); Logger.SetLogger(delegate(Logger.LogLevel level, object message) { switch (level) { case Logger.LogLevel.Info: ((BaseUnityPlugin)this).Logger.LogInfo(message); break; case Logger.LogLevel.Warning: ((BaseUnityPlugin)this).Logger.LogWarning(message); break; case Logger.LogLevel.Error: ((BaseUnityPlugin)this).Logger.LogError(message); break; default: throw new ArgumentOutOfRangeException("level", level, null); } }); } } public static class WorldGeneratorExtensions { [SpecialName] public sealed class $6FCB1B340A09A2B299219C5F5C371FD8 { [SpecialName] public static class $6F6DF1476D6644A49741E96742C809A0 { } [ExtensionMarker("$6F6DF1476D6644A49741E96742C809A0")] public void SetRenderer(RiverheimPlugin.ValheimWorldRenderer renderer) { throw null; } [ExtensionMarker("$6F6DF1476D6644A49741E96742C809A0")] public RiverheimPlugin.ValheimWorldRenderer GetRenderer() { throw null; } } private static readonly ConditionalWeakTable Renderers = new ConditionalWeakTable(); public static void SetRenderer(this WorldGenerator worldGenerator, RiverheimPlugin.ValheimWorldRenderer renderer) { Renderers.AddOrUpdate(worldGenerator, renderer); } public static RiverheimPlugin.ValheimWorldRenderer GetRenderer(this WorldGenerator worldGenerator) { if (worldGenerator == null) { return null; } Renderers.TryGetValue(worldGenerator, out var value); return value; } } internal static class CodeMatcherExtensions { [SpecialName] public sealed class $6C7DE3593F499BD1D1FE1E0A0EDB9649 { [SpecialName] public static class $F04B44C5FDA9C4CCFD20224FF826B3CB { } [ExtensionMarker("$F04B44C5FDA9C4CCFD20224FF826B3CB")] public CodeMatcher DebugLog() { throw null; } [ExtensionMarker("$F04B44C5FDA9C4CCFD20224FF826B3CB")] public CodeMatcher AdvanceToAfterZPackageConstructor(out CodeInstruction ld_ZPackage) { throw null; } [ExtensionMarker("$F04B44C5FDA9C4CCFD20224FF826B3CB")] public CodeMatcher AdvanceToAfterWorldVersionWrite(out CodeInstruction ld_World) { throw null; } [ExtensionMarker("$F04B44C5FDA9C4CCFD20224FF826B3CB")] public CodeMatcher MatchBadVersionWorldConstructor(out Label label) { throw null; } [ExtensionMarker("$F04B44C5FDA9C4CCFD20224FF826B3CB")] public CodeMatcher AdvanceToAfterDefaultWorldConstructor(out CodeInstruction ld_World) { throw null; } } private static CodeInstruction MatchStoreWithLoad(CodeInstruction instruction) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Expected O, but got Unknown //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Expected O, but got Unknown if (instruction.opcode == OpCodes.Stloc_0) { return new CodeInstruction(OpCodes.Ldloc_0, (object)null); } if (instruction.opcode == OpCodes.Stloc_1) { return new CodeInstruction(OpCodes.Ldloc_1, (object)null); } if (instruction.opcode == OpCodes.Stloc_2) { return new CodeInstruction(OpCodes.Ldloc_2, (object)null); } if (instruction.opcode == OpCodes.Stloc_3) { return new CodeInstruction(OpCodes.Ldloc_3, (object)null); } if (instruction.opcode == OpCodes.Stloc_S) { return new CodeInstruction(OpCodes.Ldloc_S, instruction.operand); } if (instruction.opcode == OpCodes.Stloc) { return new CodeInstruction(OpCodes.Ldloc, instruction.operand); } throw new InvalidOperationException($"Not a store instruction: {instruction.opcode}"); } public static CodeMatcher DebugLog(this CodeMatcher matcher) { return matcher; } public static CodeMatcher AdvanceToAfterZPackageConstructor(this CodeMatcher matcher, out CodeInstruction ld_ZPackage) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown matcher.MatchForward(true, (CodeMatch[])(object)new CodeMatch[2] { new CodeMatch((Func)((CodeInstruction instruction) => !(instruction.opcode != OpCodes.Newobj) && ((ConstructorInfo)instruction.operand).DeclaringType == typeof(ZPackage)), (string)null), new CodeMatch((Func)((CodeInstruction instruction) => CodeInstructionExtensions.IsStloc(instruction, (LocalBuilder)null)), (string)null) }); if (matcher.IsInvalid) { throw new InvalidOperationException("Error patching, unable to find zPackage instancing"); } ld_ZPackage = MatchStoreWithLoad(matcher.Instruction); return matcher.Advance(1); } public static CodeMatcher AdvanceToAfterWorldVersionWrite(this CodeMatcher matcher, out CodeInstruction ld_World) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Expected O, but got Unknown ld_World = new CodeInstruction(OpCodes.Ldarg_0, (object)null); matcher.MatchForward(true, (CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((Func)delegate(CodeInstruction instruction) { if (instruction.opcode != OpCodes.Callvirt) { return false; } return ((MethodInfo)instruction.operand).DeclaringType == typeof(ZPackage) && ((MethodInfo)instruction.operand).Name == "Write" && ((MethodInfo)instruction.operand).GetParameters()[0].ParameterType == typeof(int); }, (string)null) }); return matcher.Advance(1); } public static CodeMatcher MatchBadVersionWorldConstructor(this CodeMatcher matcher, out Label label) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Expected O, but got Unknown matcher.MatchForward(false, (CodeMatch[])(object)new CodeMatch[3] { new CodeMatch((Func)((CodeInstruction instruction) => CodeInstructionExtensions.IsLdarg(instruction, (int?)null)), (string)null), new CodeMatch((Func)((CodeInstruction instruction) => instruction.opcode == OpCodes.Ldc_I4_1 || CodeInstructionExtensions.Is(instruction, OpCodes.Ldc_I4, (object)1) || CodeInstructionExtensions.Is(instruction, OpCodes.Ldc_I4_S, (object)1)), (string)null), new CodeMatch((Func)delegate(CodeInstruction instruction) { if (instruction.opcode != OpCodes.Newobj) { return false; } ConstructorInfo constructorInfo = (ConstructorInfo)instruction.operand; if (constructorInfo.DeclaringType != typeof(World)) { return false; } ParameterInfo[] parameters = constructorInfo.GetParameters(); if (parameters.Length != 2) { return false; } return parameters[0].ParameterType == typeof(SaveWithBackups) && parameters[1].ParameterType == typeof(SaveDataError); }, (string)null) }); if (matcher.IsInvalid) { throw new InvalidOperationException("Error patching, unable to find World instancing"); } matcher.CreateLabel(ref label); return matcher; } public static CodeMatcher AdvanceToAfterDefaultWorldConstructor(this CodeMatcher matcher, out CodeInstruction ld_World) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Expected O, but got Unknown //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Expected O, but got Unknown matcher.MatchForward(true, (CodeMatch[])(object)new CodeMatch[2] { new CodeMatch((Func)delegate(CodeInstruction instruction) { if (instruction.opcode != OpCodes.Newobj) { return false; } ConstructorInfo constructorInfo = (ConstructorInfo)instruction.operand; return !(constructorInfo.DeclaringType != typeof(World)) && constructorInfo.GetParameters().Length == 0; }, (string)null), new CodeMatch((Func)((CodeInstruction instruction) => CodeInstructionExtensions.IsStloc(instruction, (LocalBuilder)null)), (string)null) }); ld_World = MatchStoreWithLoad(matcher.Instruction); return matcher.Advance(1); } } internal static class ZPackageExtensions { [SpecialName] public sealed class $3129DFD62690FF8C352496DD2A72ABBA { [SpecialName] public static class $26CCCE991BE6EACF57A91F03469C03EA { } [ExtensionMarker("$26CCCE991BE6EACF57A91F03469C03EA")] public void Write(WorldSettings settingsObject) { throw null; } [ExtensionMarker("$26CCCE991BE6EACF57A91F03469C03EA")] public WorldSettings ReadWorldSettings() { throw null; } } public static void Write(this ZPackage package, WorldSettings settingsObject) { WorldSettingSerialization.Setting[] array = WorldSettingSerialization.Serialize(settingsObject); package.WriteNumItems(array.Length); WorldSettingSerialization.Setting[] array2 = array; for (int i = 0; i < array2.Length; i++) { WorldSettingSerialization.Setting setting = array2[i]; package.Write(setting.name); switch (WorldSettingSerialization.GetSettingType(setting.name)) { case WorldSettings.SettingType.String: package.Write((string)setting.value); break; case WorldSettings.SettingType.Int: package.Write((int)setting.value); break; case WorldSettings.SettingType.Bool: package.Write((bool)setting.value); break; case WorldSettings.SettingType.Float: package.Write((float)setting.value); break; case WorldSettings.SettingType.Double: package.Write((double)setting.value); break; default: throw new ArgumentOutOfRangeException(); } } } public static WorldSettings ReadWorldSettings(this ZPackage package) { WorldSettingSerialization.Setting[] array = new WorldSettingSerialization.Setting[package.ReadNumItems()]; for (int i = 0; i < array.Length; i++) { string text = package.ReadString(); object value = WorldSettingSerialization.GetSettingType(text) switch { WorldSettings.SettingType.String => package.ReadString(), WorldSettings.SettingType.Int => package.ReadInt(), WorldSettings.SettingType.Bool => package.ReadBool(), WorldSettings.SettingType.Float => package.ReadSingle(), WorldSettings.SettingType.Double => package.ReadDouble(), _ => throw new ArgumentOutOfRangeException(), }; array[i] = new WorldSettingSerialization.Setting { name = text, value = value }; } return WorldSettingSerialization.Deserialize(array); } } } namespace Riverheim.Plugin.Compat { public static class ModCompat { public const string GUID_BETTER_CONTINENTS = "BetterContinents"; public const string GUID_EWS = "expand_world_size"; public const string GUID_EWP = "expand_world_data"; } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace Riverheim { [Serializable] public struct CommonConfig { public double worldSize; public double tileSpacing; public double mountainHeight; public double oceanDepth; public double startingAreaRadius; } internal static class DeterministicMath { private const short INLINE = 256; public const double PI = Math.PI; public const double RELATIVE_ERROR = 1E-10; private const double TWO_PI = Math.PI * 2.0; private const double HALF_PI = Math.PI / 2.0; private const double EIGHTH_OF_PI = Math.PI / 8.0; private const double TAN_PI_OVER_8 = 0.4142135623730951; private const double SQRT2 = 1.4142135623730951; private const double LN2 = 0.6931471805599453; private const double LN2_INV = 1.4426950408889634; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double GuardNaN(double x) { throw new ArgumentOutOfRangeException($"Function input outside range: {x}"); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Abs(double x) { return BitConverter.Int64BitsToDouble(BitConverter.DoubleToInt64Bits(x) & 0x7FFFFFFFFFFFFFFFL); } public static double Sqrt(double x) { if ((x < 0.0 || double.IsNaN(x)) ? true : false) { return GuardNaN(x); } if (x == 0.0 || double.IsPositiveInfinity(x)) { return x; } return x * InvSqrt(x); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double InvSqrt(double x) { double num = 0.5 * x; long num2 = BitConverter.DoubleToInt64Bits(x); num2 = 6910469410427058089L - (num2 >> 1); double num3 = BitConverter.Int64BitsToDouble(num2); num3 *= 1.5 - num * num3 * num3; num3 *= 1.5 - num * num3 * num3; return num3 * (1.5 - num * num3 * num3); } public static double Exp(double x) { if (double.IsNaN(x)) { return GuardNaN(x); } if (double.IsNegativeInfinity(x) || x < -745.133) { return 0.0; } if (double.IsPositiveInfinity(x) || x > 709.782) { return double.PositiveInfinity; } double num = x * 1.4426950408889634; long num2 = (long)(num + ((num >= 0.0) ? 0.5 : (-0.5))); double num3 = x - (double)num2 * 0.6931471805599453; return (1.0 + num3 * (1.0 + num3 * (0.5 + num3 * (1.0 / 6.0 + num3 * (1.0 / 24.0 + num3 * (1.0 / 120.0 + num3 * (1.0 / 720.0 + num3 * (0.0001984126984126984 + num3 * (2.48015873015873E-05 + num3 * 2.7557319223985893E-06))))))))) * BitConverter.Int64BitsToDouble(num2 + 1023 << 52); } public static double Log(double x) { if (double.IsNaN(x) || x < 0.0) { return GuardNaN(x); } if (x == 0.0) { return double.NegativeInfinity; } int num = 0; if (x < 2.2250738585072014E-308) { x *= 4503599627370496.0; num = -52; } long num2 = BitConverter.DoubleToInt64Bits(x); int num3 = (int)((num2 >> 52) & 0x7FF) - 1023 + num; double num4 = BitConverter.Int64BitsToDouble((num2 & 0xFFFFFFFFFFFFFL) | 0x3FF0000000000000L); if (num4 > 1.4142135623730951) { num4 *= 0.5; num3++; } double num5 = (num4 - 1.0) / (num4 + 1.0); double num6 = num5 * num5; return 2.0 * num5 * (1.0 + num6 * (1.0 / 3.0 + num6 * (0.2 + num6 * (1.0 / 7.0 + num6 * (1.0 / 9.0 + num6 * (1.0 / 11.0)))))) + (double)num3 * 0.6931471805599453; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double Pow(double x, long p) { bool flag = p < 0; if (flag) { p = -p; } double num = 1.0; double num2 = x; while (p > 0) { if ((p & 1) != 0L) { num *= num2; } num2 *= num2; p >>= 1; } if (!flag) { return num; } return 1.0 / num; } public static double Pow(double x, int p) { if (p == 0) { return 1.0; } if (x == 0.0) { if (p <= 0) { return double.PositiveInfinity; } return 0.0; } return Pow(x, (long)p); } public static double Pow(double x, double p) { if (x < 0.0) { return GuardNaN(x); } if (p == 0.0) { return 1.0; } if (x == 0.0) { if (!(p > 0.0)) { return double.PositiveInfinity; } return 0.0; } long num = (long)p; if (p == (double)num) { return Pow(x, num); } if (p == 0.5) { return Sqrt(x); } return Exp(p * Log(x)); } public static double Sin(double x) { if (!double.IsFinite(x)) { return GuardNaN(x); } x %= Math.PI * 2.0; if (x > Math.PI) { x -= Math.PI * 2.0; } else if (x < -Math.PI) { x += Math.PI * 2.0; } if (x > Math.PI / 2.0) { x = Math.PI - x; } else if (x < -Math.PI / 2.0) { x = -Math.PI - x; } double num = x * x; return x * (1.0 - num * (1.0 / 6.0 - num * (1.0 / 120.0 - num * (0.0001984126984126984 - num * (2.7557319223985893E-06 - num * (2.505210838544172E-08 - num * (1.6059043836821613E-10 - num * 7.647163731819816E-13))))))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Cos(double x) { return Sin(x + Math.PI / 2.0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Tan(double x) { return Sin(x) / Cos(x); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Asin(double x) { if (x < -1.0 || x > 1.0) { return GuardNaN(x); } return Atan2(x, Sqrt(1.0 - x * x)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Acos(double x) { if (x < -1.0 || x > 1.0) { return GuardNaN(x); } return Atan2(Sqrt(1.0 - x * x), x); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Atan(double x) { if (!double.IsFinite(x)) { return GuardNaN(x); } bool num = x < 0.0; x = Abs(x); bool num2 = x > 1.0; if (num2) { x = 1.0 / x; } bool num3 = x > 0.4142135623730951; if (num3) { x = (x - 0.4142135623730951) / (1.0 + x * 0.4142135623730951); } double num4 = x * x; double num5 = x * (1.0 - num4 * (1.0 / 3.0 - num4 * (0.2 - num4 * (1.0 / 7.0 - num4 * (1.0 / 9.0 - num4 * (1.0 / 11.0 - num4 * (1.0 / 13.0 - num4 * (1.0 / 15.0 - num4 * (1.0 / 17.0 - num4 * (1.0 / 19.0 - num4 * (1.0 / 21.0 - num4 * (1.0 / 23.0)))))))))))); if (num3) { num5 += Math.PI / 8.0; } if (num2) { num5 = Math.PI / 2.0 - num5; } if (num) { num5 = 0.0 - num5; } return num5; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Atan2(double y, double x) { if (x > 0.0) { return Atan(y / x); } if (x < 0.0) { if (!(y >= 0.0)) { return Atan(y / x) - Math.PI; } return Atan(y / x) + Math.PI; } if (y > 0.0) { return Math.PI / 2.0; } if (y < 0.0) { return -Math.PI / 2.0; } return 0.0; } } public readonly struct InertFloat { private readonly float value; private InertFloat(float value) { this.value = value; } public static implicit operator double(InertFloat f) { return f.value; } public static implicit operator InertFloat(double f) { return new InertFloat((float)f); } public override string ToString() { return $"{value}"; } } public readonly struct InertFloat2 { private readonly float x; private readonly float y; private InertFloat2(float x, float y) { this.x = x; this.y = y; } public static implicit operator Rfloat2(InertFloat2 f2) { return new Rfloat2(f2.x, f2.y); } public static implicit operator InertFloat2(Rfloat2 f2) { return new InertFloat2((float)f2.x, (float)f2.y); } public override string ToString() { return $"({x}, {y})"; } } public readonly struct Rfloat2 : IEquatable { private const MethodImplOptions INLINE = MethodImplOptions.AggressiveInlining; private const double TOLERANCE = 1E-06; public readonly double x; public readonly double y; public double Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Rmath.Sqrt(x * x + y * y); } } public double SqrLength { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return x * x + y * y; } } public bool IsNegligible { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (Rmath.Abs(x) < 1E-06) { return Rmath.Abs(y) < 1E-06; } return false; } } public static Rfloat2 Zero => new Rfloat2(0.0, 0.0); public static Rfloat2 One => new Rfloat2(1.0, 1.0); public Rfloat2 Normalized { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { double length = Length; if (!(length > 1E-06)) { return Zero; } return new Rfloat2(x / length, y / length); } } public Rfloat2(double x, double y) { this.x = x; this.y = y; } public Rfloat2(double val) : this(val, val) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator Rfloat2(Rint2 ivec) { return new Rfloat2(ivec.x, ivec.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Distance(Rfloat2 lhs, Rfloat2 rhs) { return (lhs - rhs).Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Dot(Rfloat2 lhs, Rfloat2 rhs) { return lhs.x * rhs.x + lhs.y * rhs.y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Cross(Rfloat2 lhs, Rfloat2 rhs) { return lhs.x * rhs.y - lhs.y * rhs.x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Angle(Rfloat2 a, Rfloat2 b) { double num = Rmath.Sqrt(a.SqrLength * b.SqrLength); if (num < 1E-06) { return 0.0; } return Rmath.Acos(Rmath.Clamp(Dot(a, b) / num, -1.0, 1.0)) * (double)Rmath.Sign(Cross(a, b)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 UnclampedLerp(double t, Rfloat2 a, Rfloat2 b) { return new Rfloat2(Rmath.UnclampedLerp(t, a.x, b.x), Rmath.UnclampedLerp(t, a.y, b.y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 Lerp(double t, Rfloat2 a, Rfloat2 b) { return new Rfloat2(Rmath.Lerp(t, a.x, b.x), Rmath.Lerp(t, a.y, b.y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public double DistanceTo(Rfloat2 other) { return (this - other).Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rint2 FloorToInt() { return new Rint2(Rmath.FloorToInt(x), Rmath.FloorToInt(y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rint2 CeilToInt() { return new Rint2(Rmath.CeilToInt(x), Rmath.CeilToInt(y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rint2 RoundToInt() { return new Rint2(Rmath.RoundToInt(x), Rmath.RoundToInt(y)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Rfloat2 other) { if (x.Equals(other.x)) { return y.Equals(other.y); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { if (obj is Rfloat2 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return HashCode.Combine(x, y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() { return $"({x}, {y})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rfloat2 lhs, Rfloat2 rhs) { if (Rmath.Abs(lhs.x - rhs.x) < 1E-06) { return Rmath.Abs(lhs.y - rhs.y) < 1E-06; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rfloat2 lhs, Rfloat2 rhs) { if (!(Rmath.Abs(lhs.x - rhs.x) > 1E-06)) { return Rmath.Abs(lhs.y - rhs.y) > 1E-06; } return true; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator +(Rfloat2 lhs, Rfloat2 rhs) { return new Rfloat2(lhs.x + rhs.x, lhs.y + rhs.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator -(Rfloat2 lhs, Rfloat2 rhs) { return new Rfloat2(lhs.x - rhs.x, lhs.y - rhs.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator -(Rfloat2 vec) { return new Rfloat2(0.0 - vec.x, 0.0 - vec.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator +(Rfloat2 vec, double scalar) { return new Rfloat2(vec.x + scalar, vec.y + scalar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator +(double scalar, Rfloat2 vec) { return new Rfloat2(scalar * vec.x, scalar * vec.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator -(Rfloat2 vec, double scalar) { return new Rfloat2(vec.x - scalar, vec.y - scalar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator -(double scalar, Rfloat2 vec) { return new Rfloat2(scalar - vec.x, scalar - vec.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator *(Rfloat2 lhs, Rfloat2 rhs) { return new Rfloat2(lhs.x * rhs.x, lhs.y * rhs.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator *(Rfloat2 vec, double scalar) { return new Rfloat2(vec.x * scalar, vec.y * scalar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator *(double scalar, Rfloat2 vec) { return new Rfloat2(scalar * vec.x, scalar * vec.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator /(Rfloat2 lhs, Rfloat2 rhs) { return new Rfloat2(lhs.x / rhs.x, lhs.y / rhs.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator /(Rfloat2 vec, double scalar) { return new Rfloat2(vec.x / scalar, vec.y / scalar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat2 operator /(double scalar, Rfloat2 vec) { return new Rfloat2(scalar / vec.x, scalar / vec.y); } } [Serializable] public struct Rint2 : IEquatable { private const MethodImplOptions INLINE = MethodImplOptions.AggressiveInlining; public readonly int x; public readonly int y; public static Rint2 Zero => new Rint2(0, 0); public static Rint2 One => new Rint2(1, 1); public Rint2(int x, int y) { this.x = x; this.y = y; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(Rint2 other) { if (x == other.x) { return y == other.y; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override bool Equals(object obj) { if (obj is Rint2 other) { return Equals(other); } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override int GetHashCode() { return HashCode.Combine(x, y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator ==(Rint2 left, Rint2 right) { return left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool operator !=(Rint2 left, Rint2 right) { return !left.Equals(right); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override string ToString() { return $"({x}, {y})"; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rint2 operator +(Rint2 lhs, Rint2 rhs) { return new Rint2(lhs.x + rhs.x, lhs.y + rhs.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rint2 operator -(Rint2 lhs, Rint2 rhs) { return new Rint2(lhs.x - rhs.x, lhs.y - rhs.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rint2 operator -(Rint2 vec) { return new Rint2(-vec.x, -vec.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rint2 operator +(Rint2 vec, int scalar) { return new Rint2(vec.x + scalar, vec.y + scalar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rint2 operator +(int scalar, Rint2 vec) { return new Rint2(scalar * vec.x, scalar * vec.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rint2 operator -(Rint2 vec, int scalar) { return new Rint2(vec.x - scalar, vec.y - scalar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rint2 operator -(int scalar, Rint2 vec) { return new Rint2(scalar - vec.x, scalar - vec.y); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rint2 operator *(Rint2 lhs, Rint2 rhs) { return new Rint2(lhs.x * rhs.x, lhs.y * rhs.x); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rint2 operator *(Rint2 vec, int scalar) { return new Rint2(vec.x * scalar, vec.y * scalar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rint2 operator *(int scalar, Rint2 vec) { return new Rint2(scalar * vec.x, scalar * vec.y); } } public static class Rmath { [Serializable] public struct Range { public double min; public double max; public bool IsEmpty => min.Equals(max); public bool IsPositive => max > min; public static Range Unit { get { Range result = default(Range); result.min = -1.0; result.max = 1.0; return result; } } public static Range From(double a, double b) { Range result; if (!(a <= b)) { result = default(Range); result.min = b; result.max = a; return result; } result = default(Range); result.min = a; result.max = b; return result; } } [Serializable] public struct RemapConfig { public double fromA; public double fromB; public double toA; public double toB; public Range From { get { Range result = default(Range); result.min = fromA; result.max = fromB; return result; } set { double min = value.min; double max = value.max; fromA = min; fromB = max; } } public Range To { get { Range result = default(Range); result.min = toA; result.max = toB; return result; } set { double min = value.min; double max = value.max; toA = min; toB = max; } } public bool IsEmpty { get { if (!From.IsEmpty) { return To.IsEmpty; } return true; } } } private const short INLINE = 256; public const double PI = Math.PI; public const double RAD_TO_DEG = 180.0 / Math.PI; public const double DEG_TO_RAD = Math.PI / 180.0; public const double EPSILON = 1E-06; public const double RELATIVE_TOLERANCE = 1E-10; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int FloorToInt(double f) { if (f < 0.0) { return (int)f - 1; } return (int)f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int CeilToInt(double f) { if (f > 0.0) { return (int)f + 1; } return (int)f; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int RoundToInt(double f) { return FloorToInt(f + 0.5); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Min(int a, int b) { if (a >= b) { return b; } return a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Max(int a, int b) { if (a <= b) { return b; } return a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Min(double a, double b) { if (!double.IsNaN(a) && !(a < b)) { return b; } return a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Max(double a, double b) { if (!double.IsNaN(a) && !(a > b)) { return b; } return a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Abs(double value) { return DeterministicMath.Abs(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int Sign(double value) { if (!(value < 0.0)) { return 1; } return -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Sqrt(double value) { return DeterministicMath.Sqrt(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Pow(double value, int power) { return DeterministicMath.Pow(value, power); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Pow(double value, double power) { return DeterministicMath.Pow(value, power); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Exp(double value) { return DeterministicMath.Exp(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Log(double value) { return DeterministicMath.Log(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Sin(double value) { return DeterministicMath.Sin(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Cos(double value) { return DeterministicMath.Cos(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Tan(double value) { return DeterministicMath.Tan(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Asin(double value) { return DeterministicMath.Asin(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Acos(double value) { return DeterministicMath.Acos(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Atan(double value) { return DeterministicMath.Atan(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Atan2(double y, double x) { return DeterministicMath.Atan2(y, x); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Smooth3(double t) { return t * t * (3.0 - 2.0 * t); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Smooth5(double t) { return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double UnclampedLerp(double t, double a, double b) { return a + t * (b - a); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double UnclampedLerp(double t, Range range) { return UnclampedLerp(t, range.min, range.max); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Lerp(double t, double a, double b) { return UnclampedLerp(Clamp01(t), a, b); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Lerp(double t, Range range) { return Lerp(t, range.min, range.max); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double SmoothStep(double t, double a, double b) { if (!(t <= 0.0)) { if (t >= 1.0) { return b; } return UnclampedLerp(Smooth3(t), a, b); } return a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double SmoothStep(double t, Range range) { return SmoothStep(t, range.min, range.max); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double SmootherStep(double t, double a, double b) { if (!(t <= 0.0)) { if (t >= 1.0) { return b; } return UnclampedLerp(Smooth5(t), a, b); } return a; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double SmootherStep(double t, Range range) { return SmootherStep(t, range.min, range.max); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double InverseLerp(double value, double a, double b) { if (a.Equals(b)) { return 0.5; } return Clamp01((value - a) / (b - a)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double InverseLerp(double value, Range range) { return InverseLerp(value, range.min, range.max); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Clamp(double value, double min, double max) { if (value < min) { return min; } if (!(value > max)) { return value; } return max; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Clamp(double value, Range range) { return Clamp(value, range.min, range.max); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Clamp01(double value) { if (!(value < 0.0)) { if (value > 1.0) { return 1.0; } return value; } return 0.0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Remap(double value, RemapConfig config) { return Lerp(InverseLerp(value, config.fromA, config.fromB), config.toA, config.toB); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool Within(double value, Range range) { if (value >= range.min) { return value <= range.max; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Rectifier(double value) { if (!(value > 0.0)) { return 0.0; } return value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Curvature(Rfloat2 d, Rfloat2 d2) { double num = 1.0 + d.SqrLength; return d2.Length / Sqrt(num * num * num); } } public interface IWorldGen { public struct PointData { public Biome biome; public double height; public double fertility; public double lava; public double dirt; public double pave; } Biome GetBiome(Rfloat2 pos); double GetForestation(Rfloat2 pos); PointData RenderPoint(Rfloat2 pos); } [Serializable] [CompositeConfig] public struct WorldGenerationConfig { public CommonConfig common; public WorldConfig world; public RenderingConfig rendering; } } namespace Riverheim.World { [Producer(new string[] { "CoastalProximity" })] [Fork("Region")] public class CoastalProximityStage : IGeneratorStage> { public const string OUTPUT = "CoastalProximity"; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Base")] public TileData flags; public TileData Compute() { return tiling.DistanceTransform((Tile tile) => (!tile.Data(flags).IsCoast) ? null : new double?(0.5 * tiling.Spacing)); } } public readonly struct ExtendedTileFlags { public bool IsCenter { get; } public bool IsLand => !IsWater; public bool IsWater { get { if (!IsOcean) { return IsLake; } return true; } } public bool IsOcean { get; } public bool IsLake { get; } public bool IsCoast { get; } public ExtendedTileFlags(bool isCenter, bool isOcean, bool isLake, bool isCoast) { IsCenter = isCenter; IsOcean = isOcean; IsLake = isLake; IsCoast = isCoast; } } [Producer(new string[] { "Flags/Extended" })] [Fork("Region")] public class ExtendedTileFlagStage : IGeneratorStage> { public const string OUTPUT = "Flags/Extended"; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Base")] public TileData baseFlags; [Resource("Heights/Lakes")] public TileSet lakes; public TileData Compute() { return tiling.CreateData((Tile tile) => new ExtendedTileFlags(baseFlags[tile].IsCenter, baseFlags[tile].IsOcean, lakes.Contains(tile), baseFlags[tile].IsCoast || IsLakeShore(tile, lakes))); } private bool IsLakeShore(Tile tile, TileSet lakeTiles) { if (!baseFlags[tile].IsLand) { return false; } bool isLake = lakeTiles.Contains(tile); return tile.GetNeighbors().Any((Tile neighbor) => lakeTiles.Contains(neighbor) != isLake); } } [Producer(new string[] { "Forestation" })] [Fork("Region")] public class ForestationStage : IGeneratorStage> { [Serializable] public struct Config { public double noiseScale; public NoiseField.FractalConfig noiseFractal; public double marshBonus; public double plainsRiverBonus; public Rmath.Range plainsRiverBonusProximityRange; public Rmath.Range plainsRiverBonusHeightRange; public double meadowsRiverBonus; public Rmath.Range meadowsRiverBonusProximityRange; } public const string OUTPUT = "Forestation"; [Random("Forestation")] public RandomDirectory random; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Biomes")] public TileData biomes; [Resource("Rivers/Proximity")] public TileData riverProximity; [Resource("Heights/SettledHeightMap")] public TileData heights; public TileData Compute() { NoiseField noise = random.BiasedNoise2D("Forestation", config.noiseScale).Fractal(config.noiseFractal); return tiling.CreateData((Func)delegate(Tile tile) { double result = 1.0 - noise.Sample(tile.Center); return Rmath.Clamp01(AdjustResult(biomes[tile], tile, result)); }); } private double AdjustResult(Biome biome, Tile tile, double result) { switch (biome) { case Biome.Meadows: { double meadowsRiverBonus = config.meadowsRiverBonus; meadowsRiverBonus *= 1.0 - Rmath.InverseLerp(riverProximity[tile], config.meadowsRiverBonusProximityRange); return result + meadowsRiverBonus; } case Biome.Marsh: return result + config.marshBonus; case Biome.Plains: { double plainsRiverBonus = config.plainsRiverBonus; plainsRiverBonus *= 1.0 - Rmath.InverseLerp(riverProximity[tile], config.plainsRiverBonusProximityRange); plainsRiverBonus *= 1.0 - Rmath.InverseLerp(heights[tile], config.plainsRiverBonusHeightRange); return result + plainsRiverBonus; } case Biome.Mistglade: return 0.0; default: return result; } } } public class LandmassInfo { public int border; public double area; public double coastline; public double Compactness => Math.PI * 4.0 * area / (coastline * coastline); } public struct LandmassMetrics { public int count; public double totalArea; public double maxArea; public double avgArea; public double avgCompactness; } public struct RiverMetrics { public int count; public double maxLength; public double avgLength; } public class BiomeMetrics { public double totalArea; public double innerArea; } public struct WorldMetrics { public LandmassMetrics landmassMetrics; public RiverMetrics riverMetrics; public Dictionary biomeMetrics; public double highestPoint; public double centerCoastalProximity; public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("World Metrics:"); stringBuilder.AppendLine(" Total Land: " + ToPercentage(landmassMetrics.totalArea)); stringBuilder.AppendLine($" Landmass Count: {landmassMetrics.count}"); stringBuilder.AppendLine(" Largest Landmass: " + ToPercentage(landmassMetrics.maxArea)); stringBuilder.AppendLine(" Average Landmass: " + ToPercentage(landmassMetrics.avgArea)); stringBuilder.AppendLine($" Average Compactness: {landmassMetrics.avgCompactness:0.0}"); stringBuilder.AppendLine($" Highest point: {highestPoint:0.0}m"); stringBuilder.AppendLine(); stringBuilder.AppendLine($" Total Rivers: {riverMetrics.count}"); stringBuilder.AppendLine($" Longest River: {riverMetrics.maxLength:0.0}m"); stringBuilder.AppendLine($" Average River: {riverMetrics.avgLength:0.0}m"); stringBuilder.AppendLine(); foreach (Biome key in this.biomeMetrics.Keys) { BiomeMetrics biomeMetrics = this.biomeMetrics[key]; stringBuilder.AppendLine($" {key} Total Area (Inner): {ToPercentage(biomeMetrics.totalArea)} ({ToPercentage(biomeMetrics.innerArea)})"); } return stringBuilder.ToString(); } private static string ToPercentage(double value) { return $"{value * 100.0:0.00}%"; } } [Producer(new string[] { "WorldMetrics" })] public class WorldMetricCalculator : IGeneratorStage { public const string OUTPUT = "WorldMetrics"; [Resource("MainTiling")] public Tiling tiling; [Resource("Regions/Tiling")] public Tiling[] regionTilings; [Resource("Regions/Mapping")] public SubtilingMapping[] regionMappings; [Resource("LandmassIds")] public TileData[] landmassIds; [Resource("Rivers/Splines")] public River[][] rivers; [Resource("Biomes")] public TileData[] biomes; [Resource("Heights/JoinedHeights")] public TileData heights; [Resource("CoastalProximity")] public TileData[] coastalProximity; public WorldMetrics Compute() { WorldMetrics result = default(WorldMetrics); result.landmassMetrics = ComputeLandmassMetrics(); result.riverMetrics = ComputeRiverMetrics(RegionStage.JoinRegionData(rivers)); result.biomeMetrics = ComputeBiomeMetrics(tiling, RegionStage.JoinRegionData(tiling, regionTilings, regionMappings, biomes)); result.highestPoint = tiling.Tiles.Max((Func)((Tile tile) => heights[tile])); result.centerCoastalProximity = coastalProximity[0][tiling.GetTile(Rfloat2.Zero).Value]; return result; } private static List ComputeLandmassInfos(Tiling tiling, TileData landmassIds) { Dictionary dictionary = new Dictionary(); foreach (Tile tile in tiling.Tiles) { int landmassId = tile.Data(landmassIds); if (landmassId != -1) { if (!dictionary.ContainsKey(landmassId)) { dictionary[landmassId] = new LandmassInfo(); } dictionary[landmassId].area += tile.Area; if (tile.GetNeighbors().Any((Tile neighbor) => neighbor.Data(landmassIds) != landmassId)) { dictionary[landmassId].border++; } } } foreach (LandmassInfo value in dictionary.Values) { value.coastline = (double)value.border * tiling.Spacing; } return dictionary.Values.ToList(); } private LandmassMetrics ComputeLandmassMetrics() { List list = new List(); foreach (int value in Enum.GetValues(typeof(Region))) { list.AddRange(ComputeLandmassInfos(regionTilings[value], landmassIds[value])); } LandmassMetrics result = default(LandmassMetrics); result.count = list.Count; result.totalArea = list.Sum((LandmassInfo x) => x.area) / tiling.TotalArea; result.maxArea = list.Max((LandmassInfo x) => x.area) / tiling.TotalArea; result.avgArea = list.Average((LandmassInfo x) => x.area) / tiling.TotalArea; result.avgCompactness = list.Average((LandmassInfo x) => x.Compactness); return result; } private static RiverMetrics ComputeRiverMetrics(River[] rivers) { List source = rivers.Select(RiverLength).ToList(); RiverMetrics result = default(RiverMetrics); result.count = rivers.Length; result.maxLength = ((rivers.Length != 0) ? source.Max() : 0.0); result.avgLength = ((rivers.Length != 0) ? source.Average() : 0.0); return result; } private static double RiverLength(River river) { return river.spline.Length; } private static Dictionary ComputeBiomeMetrics(Tiling tiling, TileData biomes) { Dictionary dictionary = new Dictionary(); foreach (Tile tile in tiling.Tiles) { if (!dictionary.ContainsKey(tile.Data(biomes))) { dictionary[tile.Data(biomes)] = new BiomeMetrics(); } double num = tile.Area / tiling.TotalArea; dictionary[tile.Data(biomes)].totalArea += num; if (tile.GetNeighbors().All((Tile neighbor) => neighbor.Data(biomes) == tile.Data(biomes))) { dictionary[tile.Data(biomes)].innerArea += num; } } return dictionary; } } public enum PolarProximityType { Any, North, South } public delegate double PolarProximityMap(Rfloat2 pos, PolarProximityType type = PolarProximityType.Any); [Serializable] public struct PolarBeltConfig { public double offset; public double radius; public double noiseScale; public double noiseAmplitude; } [Producer(new string[] { "PolarProximity" })] public class PolarProximityStage : IGeneratorStage { public const string OUTPUT = "PolarProximity"; [Random("PolarProximityMap")] public RandomDirectory random; [Config] public PolarBeltConfig config; public PolarProximityMap Compute() { NoiseField noise = random.Noise2D("PolarProximity", config.noiseScale); return delegate(Rfloat2 pos, PolarProximityType type) { double num = Rmath.Atan2(pos.x, (pos.y < 0.0) ? (pos.y - config.offset) : pos.y); double num2 = config.radius + Rmath.Sin(num * 20.0) * 100.0; double num3 = (pos + new Rfloat2(0.0, config.offset)).Length - num2; double num4 = (pos - new Rfloat2(0.0, config.offset)).Length - num2; double num5 = type switch { PolarProximityType.North => num3, PolarProximityType.South => num4, PolarProximityType.Any => Rmath.Max(num3, num4), _ => throw new ArgumentOutOfRangeException(), }; double num6 = num5 / config.noiseAmplitude; return num5 + Rmath.Clamp01(num6 * num6) * config.noiseAmplitude * noise.Sample(pos); }; } } public enum Region { Main, SouthPole, NorthPole } [Serializable] public struct RegionalConfig : ILaneConfig, ILaneConfig { [CompilerGenerated] private sealed class d__6 : IEnumerable<(Region, T)>, IEnumerable, IEnumerator<(Region, T)>, IEnumerator, IDisposable { private int <>1__state; private (Region, T) <>2__current; private int <>l__initialThreadId; public RegionalConfig <>4__this; public RegionalConfig <>3__<>4__this; (Region, T) IEnumerator<(Region, T)>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__6(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (Region.Main, <>4__this.main); <>1__state = 1; return true; case 1: <>1__state = -1; <>2__current = (Region.SouthPole, <>4__this.southPole); <>1__state = 2; return true; case 2: <>1__state = -1; <>2__current = (Region.NorthPole, <>4__this.northPole); <>1__state = 3; return true; case 3: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<(Region, T)> IEnumerable<(Region, T)>.GetEnumerator() { d__6 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__6(0); } d__.<>4__this = <>3__<>4__this; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<(Region, T)>)this).GetEnumerator(); } } public T main; public T southPole; public T northPole; readonly object ILaneConfig.Get(int lane) { return Get((Region)lane); } public readonly T Get(int lane) { return Get((Region)lane); } public readonly T Get(Region region) { return region switch { Region.Main => main, Region.SouthPole => southPole, Region.NorthPole => northPole, _ => default(T), }; } [IteratorStateMachine(typeof(RegionalConfig<>.d__6))] public IEnumerable<(Region, T)> All() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__6(-2) { <>3__<>4__this = this }; } } [ForkingDimension("Region", typeof(Region))] [Producer(new string[] { "Regions" })] public class RegionStage : IGeneratorStage[]> { public const string OUTPUT = "Regions"; public const string FORK_DIMENSION = "Region"; [Config] public CommonConfig commonConfig; [Config] public PolarBeltConfig polarBeltConfig; [Resource("MainTiling")] public Tiling tiling; [Resource("TilingLods")] public Tiling[] tilingLods; [Resource("PolarProximity")] public PolarProximityMap polarProximity; public TileData[] Compute() { TileData[] array = new TileData[tilingLods.Length + 1]; for (int i = 0; i < array.Length; i++) { array[i] = ComputeLod(i); } return array; } private TileData ComputeLod(int lod) { if (lod == 0) { MutableTileData mutableTileData = tiling.CreateData((Tile tile) => GetRegion(tile, polarProximity)); FixOrphanRegions(mutableTileData); return mutableTileData; } return tilingLods[lod - 1].CreateData((Tile tile) => GetRegion(tile, polarProximity)); } private static Region GetRegion(Tile tile, PolarProximityMap polarProximity) { if (polarProximity(tile.Center, PolarProximityType.North) > 0.0) { return Region.NorthPole; } if (polarProximity(tile.Center, PolarProximityType.South) > 0.0) { return Region.SouthPole; } return Region.Main; } private Rfloat2 RegionOrigin(Region region) { double num = Rmath.Lerp(0.5, commonConfig.worldSize, polarBeltConfig.radius - polarBeltConfig.offset); return region switch { Region.Main => Rfloat2.Zero, Region.NorthPole => new Rfloat2(0.0, num), Region.SouthPole => new Rfloat2(0.0, 0.0 - num), _ => throw new ArgumentOutOfRangeException(), }; } private void FixOrphanRegions(MutableTileData regions) { MutableTileData mutableTileData = MarkRegionComponents(regions); Dictionary dictionary = new Dictionary(); foreach (Region value in Enum.GetValues(typeof(Region))) { Tile? tile = tiling.GetTile(RegionOrigin(value)); if (tile.HasValue) { Tile valueOrDefault = tile.GetValueOrDefault(); dictionary[value] = mutableTileData[valueOrDefault]; } } TileQueue tileQueue = new TileQueue(tiling); foreach (Tile tile2 in tiling.Tiles) { Region region2 = regions[tile2]; if (mutableTileData[tile2] != dictionary[region2]) { continue; } tileQueue.Enqueue(tile2); while (tileQueue.Count > 0) { foreach (Tile neighbor in tileQueue.Dequeue().GetNeighbors()) { if (mutableTileData[neighbor] != dictionary[regions[neighbor]]) { regions[neighbor] = region2; mutableTileData[neighbor] = dictionary[region2]; tileQueue.Enqueue(neighbor); } } } } } private MutableTileData MarkRegionComponents(TileData regions) { MutableTileData mutableTileData = tiling.CreateData(-1); int num = 0; TileQueue tileQueue = new TileQueue(tiling); foreach (Tile tile in tiling.Tiles) { if (mutableTileData[tile] != -1) { continue; } mutableTileData[tile] = num; tileQueue.Enqueue(tile); while (tileQueue.Count > 0) { foreach (Tile neighbor in tileQueue.Dequeue().GetNeighbors()) { if (regions[neighbor] == regions[tile] && mutableTileData[neighbor] == -1) { mutableTileData[neighbor] = num; tileQueue.Enqueue(neighbor); } } } num++; } return mutableTileData; } public static TileData JoinRegionData(Tiling tiling, Tiling[] regionTilings, SubtilingMapping[] regionMappings, TileData[] regionData) { MutableTileData mutableTileData = tiling.CreateData(); foreach (int value in Enum.GetValues(typeof(Region))) { SubtilingMapping subtilingMapping = regionMappings[value]; foreach (Tile tile in regionTilings[value].Tiles) { mutableTileData[subtilingMapping.GetParentTile(tile)] = regionData[value][tile]; } } return mutableTileData; } public static T[] JoinRegionData(T[][] regionData) { List list = new List(); foreach (int value in Enum.GetValues(typeof(Region))) { list.AddRange(regionData[value]); } return list.ToArray(); } } public readonly struct TileFlags { public bool IsCenter { get; } public bool IsLand { get; } public bool IsOcean => !IsLand; public bool IsCoast { get; } public TileFlags(bool isLand, bool isCenter, bool isCoast) { IsLand = isLand; IsCenter = isCenter; IsCoast = isCoast; } } [Producer(new string[] { "Flags/Base" })] [Fork("Region")] public class TileFlagStage : IGeneratorStage> { public const string OUTPUT = "Flags/Base"; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("LandmassIds")] public TileData landmassIds; public TileData Compute() { return tiling.CreateData((Tile tile) => new TileFlags(IsLandTile(tile), tile.Id == tiling.GetTile(Rfloat2.Zero)?.Id, IsCoastalTile(tile))); } private bool IsLandTile(Tile tile) { return tile.Data(landmassIds) != -1; } private bool IsCoastalTile(Tile tile) { foreach (Tile neighbor in tile.GetNeighbors()) { if (IsLandTile(tile) != IsLandTile(neighbor)) { return true; } } return false; } } [Producer(new string[] { "TravelDistance" })] [Fork("Region")] public class TravelDistanceStage : IGeneratorStage> { [Serializable] public struct Config { public bool normalize; public double landCost; public double riverCost; public double oceanCost; public double embarkCost; public FrontierRaymarchingConfig frontier; } [Serializable] public struct FrontierRaymarchingConfig { public int rays; public int maxSteps; public double stepRate; public double noiseScale; public double noiseMagnitude; } public const string OUTPUT = "TravelDistance"; [Lane] public Region region; [Random("TravelDistance")] public RandomDirectory random; [LaneConfig] public Config config; [Resource("MainTiling")] public Tiling mainTiling; [Resource("Regions")] public TileData[] regions; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Base")] public TileData flags; [Resource("PrunedRivers")] public TileData rivers; public TileData Compute() { TileSet frontier = Frontier(); if (frontier.Count == 0) { return tiling.CreateData((Func)((Tile tile) => tile.Center.Length)); } NoiseField noise = config.frontier.noiseMagnitude * random.Noise2D("FrontierNoise", config.frontier.noiseScale); TileData tileData = tiling.DistanceTransform((Tile tile) => (!frontier.Contains(tile)) ? null : new double?(tile.Center.Length * (1.0 + noise.Sample(tile.Center))), (Tile _) => true, (Tile t1, Tile t2) => Rfloat2.Distance(t1.Center, t2.Center) * EdgeCostMultiplier(t1, t2)); if (!config.normalize) { return tileData; } double correction = GetAverageRealDistanceRate(tileData); return tileData.Transform((Func)((InertFloat distance) => correction * (double)distance)); } private double GetAverageRealDistanceRate(TileData distances) { double num = 0.0; int num2 = 0; foreach (Tile tile in tiling.Tiles) { if (tile.HopsToBounds <= 0) { num += tile.Center.SqrLength / ((double)distances[tile] * (double)distances[tile]); num2++; } } return num / (double)num2; } private TileSet Frontier() { Tile? tile2 = tiling.GetTile(Rfloat2.Zero); if (tile2.HasValue) { Tile valueOrDefault = tile2.GetValueOrDefault(); return TileSet.Of(valueOrDefault); } TileSet tileSet = new TileSet(tiling); if (config.frontier.rays == 0) { return tileSet; } TileData tileData = mainTiling.DistanceTransform((Tile tile) => (regions[0][tile] != region) ? null : new double?(0.0), (Tile _) => true, (Tile t1, Tile t2) => Rfloat2.Distance(t1.Center, t2.Center)); double spacing = tiling.Spacing; for (int i = 0; i < config.frontier.rays; i++) { Direction direction = new Direction(Math.PI * 2.0 * (double)i / (double)config.frontier.rays); Rfloat2 zero = Rfloat2.Zero; for (int j = 0; j < config.frontier.maxSteps; j++) { tile2 = tiling.GetTile(zero); if (tile2.HasValue) { Tile valueOrDefault2 = tile2.GetValueOrDefault(); tileSet.Add(valueOrDefault2); break; } tile2 = mainTiling.GetTile(zero); double num; if (tile2.HasValue) { Tile valueOrDefault3 = tile2.GetValueOrDefault(); num = tileData[valueOrDefault3]; } else { num = spacing; } double num2 = num; zero += Rmath.Max(spacing, num2 * config.frontier.stepRate) * direction.ToVector(); } } return tileSet; } private double EdgeCostMultiplier(Tile t1, Tile t2) { if (flags[t1].IsLand != flags[t2].IsLand) { return config.embarkCost; } if (flags[t1].IsOcean || flags[t2].IsOcean) { return config.oceanCost; } if (rivers[t1].IsRiver() || rivers[t2].IsRiver()) { return config.riverCost; } return config.landCost; } } [Serializable] [CompositeConfig] public record WorldConfig { public WorldVerticesCalculator.Config tiling; public PolarBeltConfig polar; public RegionalConfig travel; public LandmassConfig landmass; public RiverConfig rivers; public HeightConfig height; public BiomeConfig biomes; public Riverheim.World.Splats.SplatConfig splats; public RegionalConfig forestation; } public class WorldState { public readonly Tiling tiling; public readonly TriangleMesh interpolationMesh; public readonly TileData heights; public readonly TileData biomes; public readonly TileData forestation; public readonly River[] rivers; public readonly Splat[] splats; public WorldState(Tiling tiling, TriangleMesh interpolationMesh, TileData heights, TileData biomes, TileData forestation, River[] rivers, Splat[] splats) { this.tiling = tiling; this.interpolationMesh = interpolationMesh; this.heights = heights; this.biomes = biomes; this.forestation = forestation; this.rivers = rivers; this.splats = splats; } } [Producer(new string[] { "WorldState" })] public class WorldStateCompiler : IGeneratorStage { public const string OUTPUT = "WorldState"; [Resource("MainTiling")] public Tiling tiling; [Resource("MainInterpolationMesh")] public TriangleMesh interpolationMesh; [Resource("Regions/Tiling")] public Tiling[] regionTilings; [Resource("Regions/Mapping")] public SubtilingMapping[] regionMappings; [Resource("Heights/JoinedHeights")] public TileData heights; [Resource("Biomes")] public TileData[] biomes; [Resource("Rivers/Splines")] public River[][] rivers; [Resource("Splats")] public Splat[] splats; [Resource("Forestation")] public TileData[] forestation; public WorldState Compute() { return new WorldState(tiling, interpolationMesh, heights, RegionStage.JoinRegionData(tiling, regionTilings, regionMappings, biomes), RegionStage.JoinRegionData(tiling, regionTilings, regionMappings, forestation), RegionStage.JoinRegionData(rivers), splats); } } } namespace Riverheim.World.Tilings { [Producer(new string[] { "MainTiling", "MainInterpolationMesh" })] public class MainTilingStage : IGeneratorStage<(Tiling, TriangleMesh)> { public const string TILING = "MainTiling"; public const string MESH = "MainInterpolationMesh"; [Resource("WorldVertices")] public WorldVertices[] vertexLayers; public (Tiling, TriangleMesh) Compute() { WorldVertices worldVertices = vertexLayers[0]; BarycentricDualTilingBuilder barycentricDualTilingBuilder = new BarycentricDualTilingBuilder(worldVertices.vertices, worldVertices.spacing); return (barycentricDualTilingBuilder.Build(), barycentricDualTilingBuilder.BuildMesh()); } } [Producer(new string[] { "Regions/Tiling", "Regions/Mapping" })] [Fork("Region")] public class RegionTilingStage : IGeneratorStage<(Tiling, SubtilingMapping)> { public const string TILING = "Regions/Tiling"; public const string MAPPING = "Regions/Mapping"; [Lane] public Region region; [Resource("MainTiling")] public Tiling tiling; [Resource("Regions")] public TileData[] regions; public (Tiling, SubtilingMapping) Compute() { SubtilingMapping mapping; return (tiling.CreateSubTiling((Tile tile) => regions[0][tile] == region, out mapping), mapping); } } [Producer(new string[] { "TilingLods" })] public class TilingLodStage : IGeneratorStage { public const string OUTPUT = "TilingLods"; [Resource("WorldVertices")] public WorldVertices[] worldVertices; public Tiling[] Compute() { Tiling[] array = new Tiling[this.worldVertices.Length - 1]; for (int i = 0; i < array.Length; i++) { WorldVertices worldVertices = this.worldVertices[i + 1]; VoronoiTilingBuilder voronoiTilingBuilder = new VoronoiTilingBuilder(worldVertices.vertices, worldVertices.spacing, worldVertices.boundingRadius); array[i] = voronoiTilingBuilder.Build(); } return array; } } public struct WorldVertices { public double spacing; public double boundingRadius; public InertFloat2[] vertices; } [Producer(new string[] { "WorldVertices" })] public class WorldVerticesCalculator : IGeneratorStage { [Serializable] public struct Config { public int lodLevels; public double lodScale; } public const string OUTPUT = "WorldVertices"; [Random("WorldVertices")] public RandomDirectory random; [Config] public CommonConfig commonConfig; [Config] public Config config; public WorldVertices[] Compute() { WorldVertices[] array = new WorldVertices[1 + config.lodLevels]; for (int i = 0; i < array.Length; i++) { double num = commonConfig.tileSpacing * Rmath.Pow(config.lodScale, i); double boundingRadius = commonConfig.worldSize + Rmath.Sqrt(3.0) * num; InertFloat2[] vertices = (from point in new PoissonDiscSampler(random.Sequence(i.ToString()), boundingRadius, num / Rmath.Sqrt(2.0)).GetPoints() where point.SqrLength < boundingRadius * boundingRadius select point).Select((Func)((Rfloat2 point) => point)).ToArray(); array[i] = new WorldVertices { spacing = num, boundingRadius = boundingRadius, vertices = vertices }; } return array; } } } namespace Riverheim.World.Splats { public record Crag : Splat { public InertFloat radius; public InertFloat terrace; public InertFloat cliffiness; } public record Pond : Splat { public InertFloat radius; } [Producer(new string[] { "Splats/RiverSources" })] [Fork("Region")] public class RiverSourceSplatStage : IGeneratorStage { [Serializable] public struct Config { public AllBiomeConfig allowedBiomes; public CragConfig crags; public PondConfig ponds; } [Serializable] public struct SpawnFeatureAffinityConfig { public enum AffinityType { None, LowerBound, UpperBound, Both } public AffinityType type; public Rmath.Range lowerBound; public Rmath.Range upperBound; } [Serializable] public struct CragConfig { public double frequency; public SpawnFeatureAffinityConfig height; public SpawnFeatureAffinityConfig riverLength; public CragRandomizer.Config random; } [Serializable] public struct PondConfig { public double frequency; public SpawnFeatureAffinityConfig height; public SpawnFeatureAffinityConfig riverLength; public double stepback; public PondRandomizer.Config random; } public const string OUTPUT = "Splats/RiverSources"; [Random("Splats/RiverSources")] public RandomDirectory random; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Regions/Mapping")] public SubtilingMapping mapping; [Resource("MainTiling")] public Tiling mainTiling; [Resource("MainInterpolationMesh")] public TriangleMesh mesh; [Resource("Rivers/Splines")] public River[] rivers; [Resource("Biomes/PostprocessedHeightMap")] public TileData heights; [Resource("Biomes")] public TileData biomes; public Splat[] Compute() { List list = new List(); List list2 = new List(); RandomIndexer randomIndexer = random.Indexer("Crags/Spawn"); CragRandomizer cragRandomizer = new CragRandomizer(random.Sub("Crags"), config.crags.random); RandomIndexer randomIndexer2 = random.Indexer("Ponds/Spawn"); PondRandomizer pondRandomizer = new PondRandomizer(random.Sub("Ponds"), config.ponds.random); BarycentricInterpolator barycentricInterpolator = new BarycentricInterpolator(mainTiling, mesh); River[] array = rivers; for (int i = 0; i < array.Length; i++) { River river = array[i]; Rfloat2 pos = river.spline.EvaluatePosition(1.0); Tile? tile = tiling.GetTile(pos); if (!tile.HasValue) { continue; } Tile valueOrDefault = tile.GetValueOrDefault(); if (config.allowedBiomes.Get(biomes[valueOrDefault])) { double num = barycentricInterpolator.InterpolateOnSubtilingData(pos, heights, mapping); double length = river.spline.Length; double frequency = config.crags.frequency; frequency *= SpawnProb(num, config.crags.height); frequency *= SpawnProb(length, config.crags.riverLength); if (randomIndexer.Single(valueOrDefault.Id).Value < frequency) { list.Add(cragRandomizer.Generate(valueOrDefault.Id, pos, num)); } double frequency2 = config.ponds.frequency; frequency2 *= SpawnProb(num, config.ponds.height); frequency2 *= SpawnProb(length, config.ponds.riverLength); if (randomIndexer2.Single(valueOrDefault.Id).Value < frequency2) { Rfloat2 pos2 = river.spline.EvaluatePosition(1.0 - config.ponds.stepback / length); list2.Add(pondRandomizer.Generate(valueOrDefault.Id, pos2)); } } } List list3 = new List(); list3.AddRange(list); list3.AddRange(list2); return list3.ToArray(); } private static double SpawnProb(double value, SpawnFeatureAffinityConfig config) { return config.type switch { SpawnFeatureAffinityConfig.AffinityType.LowerBound => Rmath.Smooth3(Rmath.InverseLerp(value, config.lowerBound)), SpawnFeatureAffinityConfig.AffinityType.UpperBound => 1.0 - Rmath.Smooth3(Rmath.InverseLerp(value, config.upperBound)), SpawnFeatureAffinityConfig.AffinityType.Both => Rmath.Smooth3(Rmath.InverseLerp(value, config.lowerBound)) - Rmath.Smooth3(Rmath.InverseLerp(value, config.upperBound)), SpawnFeatureAffinityConfig.AffinityType.None => 1.0, _ => 0.0, }; } } public class CragRandomizer { [Serializable] public struct Config { public Rmath.Range height; public Rmath.Range radius; public Rmath.Range terrace; } private readonly Config config; private readonly RandomIndexer height; private readonly RandomIndexer radius; private readonly RandomIndexer terrace; public CragRandomizer(RandomDirectory random, Config config) { this.config = config; height = random.Indexer("Height"); radius = random.Indexer("Radius"); terrace = random.Indexer("Terrace"); } public Crag Generate(int id, Rfloat2 pos, double baseHeight) { double num = height.Single(id).Range(config.height); return new Crag { pos = pos, height = baseHeight + num, radius = radius.Single(id).Range(config.radius), terrace = num * terrace.Single(id).Range(config.terrace), cliffiness = 0.0 }; } } public class PondRandomizer { [Serializable] public struct Config { public Rmath.Range height; public Rmath.Range radius; } private readonly Config config; private readonly RandomIndexer height; private readonly RandomIndexer radius; public PondRandomizer(RandomDirectory random, Config config) { this.config = config; height = random.Indexer("Height"); radius = random.Indexer("Radius"); } public Pond Generate(int id, Rfloat2 pos) { return new Pond { pos = pos, height = height.Single(id).Range(config.height), radius = radius.Single(id).Range(config.radius) }; } } public record Plateau : Splat { public InertFloat2 p1; public InertFloat2 p2; public InertFloat radius; public InertFloat2 incline; public Direction bulgeDirection; public InertFloat2 bulgeMagnitude; public double maxNeighborHeight; public bool inverted; } [Serializable] public struct PlateauConfig { public double spawnNoiseScale; public PlateauMountainPeakConfig mountainPeaks; public PlateauSeasideCliffConfig coastalCliffs; public PlateauHillConfig hills; } [Serializable] public struct PlateauMountainPeakConfig { public double spawnRate; public double minHeightDiff; public Rmath.Range size; public ClampingConfig gradientClamping; public Rpoint2 bulge; public double cragThreshold; public Rmath.Range cragHeight; public double cragRadius; public double cragTerrace; public AllBiomeConfig allowedBiomes; } [Serializable] public struct PlateauSeasideCliffConfig { public double spawnRate; public double neighborHeight; public double minHeight; public double maxHeight; public Rmath.Range size; public ClampingConfig gradientClamping; public Rmath.Range bulge; } [Serializable] public struct PlateauHillConfig { public double spawnRate; public double minHeightDiff; public Rmath.Range size; public double maxLength; public ClampingConfig gradientClamping; public Rpoint2 bulge; public AllBiomeConfig allowedBiomes; } [Serializable] public struct ClampingConfig { public enum Mode { Linear, Asymptotic } public Mode mode; public double min; public double max; public double midpoint; public double pow; public double rate; } [Producer(new string[] { "Splats/Plateaus" })] [Fork("Region")] public class PlateauStage : IGeneratorStage { public const string OUTPUT = "Splats/Plateaus"; [Random("Plateaus")] public RandomDirectory random; [LaneConfig] public PlateauConfig config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Biomes/PostprocessedHeightMap")] public TileData heights; [Resource("Biomes")] public TileData biomes; public Splat[] Compute() { List list = new List(); TileSet occupied = new TileSet(tiling); AddMountainPeaks(list, occupied); AddCoastalCliffs(list, occupied); AddHills(list, occupied); return list.ToArray(); } private void AddMountainPeaks(List result, TileSet occupied) { PlateauMountainPeakConfig cfg = config.mountainPeaks; NoiseField noiseField = random.BiasedNoise2D("SpawnRate/MountainPeaks", config.spawnNoiseScale); RandomIndexer randomIndexer = random.Indexer("Size"); foreach (Tile tile in tiling.Tiles) { if (cfg.allowedBiomes.Get(biomes[tile]) && !(noiseField.Sample(tile.Center) > cfg.spawnRate) && !tile.GetNeighbors().Any((Tile neighbor) => !cfg.allowedBiomes.Get(biomes[neighbor])) && !tile.GetNeighbors().Any((Tile neighbor) => (double)neighbor.Data(heights) > (double)tile.Data(heights) - cfg.minHeightDiff) && occupied.Add(tile)) { Rfloat2 gradient = HeightUtility.ComputeRoughness(tile, heights).Gradient; if (gradient.Length < cfg.cragThreshold) { result.Add(new Crag { pos = tile.Center, height = (double)tile.Data(heights) + randomIndexer.Single(tile.Id).Range(cfg.cragHeight), radius = cfg.cragRadius, terrace = cfg.cragTerrace, cliffiness = 1.0 }); } else { result.Add(new Plateau { pos = tile.Center, height = tile.Data(heights), radius = Rmath.Lerp(randomIndexer.Single(tile.Id).Value, cfg.size), incline = Clamp(gradient, cfg.gradientClamping), bulgeDirection = new Direction(gradient), bulgeMagnitude = cfg.bulge }); } } } } private void AddCoastalCliffs(List result, TileSet occupied) { NoiseField noiseField = random.BiasedNoise2D("SpawnRate/CoastalCliffs", config.spawnNoiseScale); RandomIndexer randomIndexer = random.Indexer("Size"); RandomIndexer randomIndexer2 = random.Indexer("PlateauBulge"); TileSet tileSet = new TileSet(tiling); List<(Tile, Tile)> list = new List<(Tile, Tile)>(); foreach (Tile tile2 in tiling.Tiles) { if ((double)tile2.Data(heights) < config.coastalCliffs.minHeight || (double)tile2.Data(heights) > config.coastalCliffs.maxHeight || noiseField.Sample(tile2.Center) > config.coastalCliffs.spawnRate || tile2.GetNeighbors().All((Tile neighbor) => (double)neighbor.Data(heights) >= config.coastalCliffs.neighborHeight) || !occupied.Add(tile2)) { continue; } tileSet.Add(tile2); foreach (Tile neighbor in tile2.GetNeighbors()) { if (tileSet.Contains(neighbor)) { list.Add((tile2, neighbor)); } } } foreach (var item3 in list) { Tile item = item3.Item1; Tile item2 = item3.Item2; Rfloat2 v = 0.5 * (item.Center + item2.Center); double num = 0.5 * ((double)item.Data(heights) + (double)item2.Data(heights)); double num2 = Rmath.Lerp(randomIndexer.Single(item.Id, item2.Id).Value, config.coastalCliffs.size); Rfloat2 rfloat = heights.InclineVector(item, item2); List list2 = item.GetNeighbors().Intersect(item2.GetNeighbors()).ToList(); if (list2.Count != 2) { continue; } int num3 = list2.Count((Tile neighbor) => (double)neighbor.Data(heights) >= config.coastalCliffs.neighborHeight); if (num3 != 0) { if (num3 != 1) { continue; } Tile tile = list2.First((Tile neighbor) => (double)neighbor.Data(heights) >= config.coastalCliffs.neighborHeight); rfloat += Clamp(HeightUtility.InclineVector(v, num, tile.Center, tile.Data(heights)), config.coastalCliffs.gradientClamping); } else { int num4 = Rmath.Sign(Rfloat2.Dot(Normal(item.Center, item2.Center), list2[1].Center - list2[0].Center)); rfloat += num4 * heights.InclineVector(list2[0], list2[1]); } Rfloat2 rfloat2 = 0.5 * (item.Center + item2.Center); result.Add(new Plateau { pos = rfloat2, p1 = item.Center - rfloat2, p2 = item2.Center - rfloat2, height = num, radius = num2, incline = rfloat, bulgeDirection = new Direction(rfloat), bulgeMagnitude = randomIndexer2.Double(item.Id, item2.Id).Range(config.coastalCliffs.bulge) }); } } private void AddHills(List result, TileSet occupied) { NoiseField noiseField = random.BiasedNoise2D("SpawnRate/Hills", config.spawnNoiseScale); RandomIndexer randomIndexer = random.Indexer("Size"); RandomIndexer randomIndexer2 = random.Indexer("Length"); foreach (Tile tile in tiling.Tiles) { if (config.hills.allowedBiomes.Get(tile.Data(biomes)) && !(noiseField.Sample(tile.Center) > config.hills.spawnRate) && !tile.GetNeighbors().Any((Tile neighbor) => (double)neighbor.Data(heights) > (double)tile.Data(heights) - config.hills.minHeightDiff) && occupied.Add(tile)) { Rfloat2 rfloat = Clamp(HeightUtility.ComputeRoughness(tile, heights).Gradient, config.hills.gradientClamping); Rfloat2 rfloat2 = rfloat.Normalized * randomIndexer2.Single(tile.Id).Value * 0.5 * config.hills.maxLength; result.Add(new Plateau { pos = tile.Center, p1 = rfloat2, p2 = -rfloat2, height = tile.Data(heights), radius = Rmath.Lerp(randomIndexer.Single(tile.Id).Value, config.hills.size), incline = rfloat, bulgeDirection = new Direction(rfloat), bulgeMagnitude = config.hills.bulge }); } } } private static Rfloat2 Normal(Rfloat2 v1, Rfloat2 v2) { Rfloat2 rfloat = v2 - v1; return new Rfloat2(rfloat.y, 0.0 - rfloat.x); } private static Rfloat2 Clamp(Rfloat2 vector, ClampingConfig config) { if (vector.IsNegligible) { return config.min * new Rfloat2(-1.0, 0.0); } double length = vector.Length; double num = config.max - config.min; return config.mode switch { ClampingConfig.Mode.Linear => Rmath.Clamp(length, config.min, config.max), ClampingConfig.Mode.Asymptotic => config.min + num * Functions.Saturation(length / num, config.midpoint, config.pow), _ => throw new ArgumentOutOfRangeException(), } / length * vector; } } [Serializable] [CompositeConfig] public record SplatConfig { public RegionalConfig plateaus; public RegionalConfig riverSourceSplats; } public abstract record Splat { public InertFloat2 pos; public InertFloat height; } [Producer(new string[] { "Splats" })] public class SplatJoinStage : IGeneratorStage { public const string OUTPUT = "Splats"; [Resource("Splats/Plateaus")] public Splat[][] regionalRoughnessSplats; [Resource("Splats/RiverSources")] public Splat[][] regionalRiverSourceSplats; public Splat[] Compute() { List list = new List(); list.AddRange(regionalRoughnessSplats.SelectMany((Splat[] splats) => splats)); list.AddRange(regionalRiverSourceSplats.SelectMany((Splat[] splats) => splats)); return list.ToArray(); } } } namespace Riverheim.World.Rivers { public struct Channel { public TileList path; public double width; } [Producer(new string[] { "Rivers/Channels" })] [Fork("Region")] public class ChannelStage : IGeneratorStage { [Serializable] public struct Config { public bool allowed; public double width; } public const string OUTPUT = "Rivers/Channels"; [LaneConfig] public Config config; [Random("Rivers/Channels")] public RandomDirectory random; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Base")] public TileData flags; [Resource("Landmasses/MergedPartitions")] public TileData partitions; public Channel[] Compute() { List list = new List(); if (!config.allowed) { return list.ToArray(); } RandomIndexer selectionRandom = random.Indexer("Selection"); foreach (TileSet faultLine in GetFaultLines()) { if (MakeChannel(faultLine, selectionRandom, out var channel)) { list.Add(channel); } } return list.ToArray(); } private List GetFaultLines() { Dictionary dictionary = new Dictionary(); foreach (Tile tile in tiling.Tiles) { if (tile.Data(flags).IsOcean) { continue; } int num = partitions[tile]; foreach (Tile neighbor in tile.GetNeighbors()) { if (neighbor.Data(flags).IsOcean) { continue; } int num2 = partitions[neighbor]; if (num != num2) { Rint2 key = ((num > num2) ? new Rint2(num2, num) : new Rint2(num, num2)); if (!dictionary.ContainsKey(key)) { dictionary[key] = new TileSet(tiling); } dictionary[key].Add(tile); } } } return new List(dictionary.Values); } private bool MakeChannel(TileSet faultLine, RandomIndexer selectionRandom, out Channel channel) { (TileSet, TileSet) oceanCoastalTiles = GetOceanCoastalTiles(faultLine); if (oceanCoastalTiles.Item1.Count == 0 || oceanCoastalTiles.Item2.Count == 0) { channel = default(Channel); return false; } Tile origin = Select(oceanCoastalTiles.Item1, selectionRandom); Tile target = Select(oceanCoastalTiles.Item2, selectionRandom); TileList tileList = TilingUtility.FindPathToTile(origin, target, (Tile tile) => faultLine.Contains(tile) || tile == target); if (tileList == null) { channel = default(Channel); return false; } channel = new Channel { path = tileList, width = config.width }; return true; } private Tile Select(TileSet tiles, RandomIndexer selectionRandom) { TileHeap tileHeap = new TileHeap(tiling, HeapType.Min); foreach (Tile item in tiles.Unordered()) { tileHeap.AddOrUpdate(item, selectionRandom.Single(item.Id).Value); } return tileHeap.Pop(); } private (TileSet, TileSet) GetOceanCoastalTiles(TileSet faultLine) { TileList tileList = new TileList(tiling); foreach (Tile item in faultLine.Unordered()) { if (!item.Data(flags).IsCoast) { continue; } foreach (Tile neighbor in item.GetNeighbors()) { if (neighbor.Data(flags).IsOcean) { tileList.Add(neighbor); } } } TileSet tileSet = new TileSet(tiling); TileSet tileSet2 = new TileSet(tiling); if (tileList.Count == 0) { return (tileSet, tileSet2); } TileSet tileSet3 = TileSet.Of(tileList[0]); foreach (Tile neighbor2 in tileList[0].GetNeighbors(3, (Tile neighbor) => neighbor.Data(flags).IsOcean)) { tileSet3.Add(neighbor2); } foreach (Tile item2 in tileList) { (tileSet3.Contains(item2) ? tileSet : tileSet2).Add(item2); } return (tileSet, tileSet2); } } [Serializable] public struct PrecipitationConfig { public double noiseScale; public double variability; public double spawnMinRadius; public double spawnMaxRadius; public double basinContribution; public double minimum; } [Producer(new string[] { "Precipitation" })] [Fork("Region")] public class PrecipitationStage : IGeneratorStage> { public const string OUTPUT = "Precipitation"; [Random("PrecipitationMap")] public RandomDirectory random; [LaneConfig] public PrecipitationConfig config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Landmasses/Artifacts")] public TileData artifacts; public TileData Compute() { NoiseField noise = random.Noise2D("Noise", config.noiseScale); return tiling.CreateData((Func)delegate(Tile tile) { double num = Rmath.Clamp(config.variability * noise.Sample(tile.Center), Rmath.Lerp(Rmath.InverseLerp(tile.Center.Length, config.spawnMinRadius, config.spawnMaxRadius), 1.0, -1.0), 1.0); if (tile.Data(artifacts).isBasin) { num += config.basinContribution; } return Rmath.Max(config.minimum, 1.0 + num); }); } } [Serializable] [CompositeConfig] public record RiverConfig { public RegionalConfig precipitation; public RegionalConfig origins; public RegionalConfig floodfill; public RegionalConfig width; public RegionalConfig prune; public RegionalConfig curiosities; public RegionalConfig channels; public RegionalConfig polylineIndex; public RegionalConfig meander; public RegionalConfig splines; } [Serializable] public struct RiverCuriosityConfig { public double sourceMult; public double sourceMinLength; public double sourceMaxLength; public double proximityMult; public double proximityScanRange; public double proximityDifferentBasinsMultiplier; public double proximityMinRatio; public double proximityMaxRatio; public double angleMult; public double angleMinDegrees; public double angleMaxDegrees; public double angleNormWidth; public double smoothingRange; } [Producer(new string[] { "Rivers/Curiosities" })] [Fork("Region")] public class RiverCuriosityStage : IGeneratorStage> { private class CuriosityData { public readonly Dictionary lengthsById; public InertFloat score; public double Length { get { double num = 0.0; foreach (double value in lengthsById.Values) { num += value; } return num; } } public CuriosityData(Dictionary lengths) { lengthsById = ((lengths == null) ? new Dictionary() : new Dictionary(lengths)); score = 0.0; } } public const string OUTPUT = "Rivers/Curiosities"; [Config] public CommonConfig commonConfig; [LaneConfig] public RiverCuriosityConfig config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("PrunedRivers")] public TileData rivers; [Resource("RiverTileMetrics")] public TileData basins; private MutableTileData data; public TileData Compute() { data = tiling.CreateData(); MutableTileData mutableTileData = tiling.CreateData(); TileList tileList = FindMouths(); foreach (Tile item in tileList) { MarkLengths(item); } foreach (Tile item2 in tileList) { MarkScores(item2); } foreach (Tile item3 in tileList) { SmoothScores(item3, mutableTileData); } return mutableTileData; } private TileList FindMouths() { TileList tileList = new TileList(tiling); foreach (Tile tile in tiling.Tiles) { if (tile.Data(rivers) != null && tile.Data(rivers).isRiverMouth) { tileList.Add(tile); } } return tileList; } private void MarkLengths(Tile tile, Dictionary parentLengths = null, double segmentLength = 0.0) { tile.Data(data) = new CuriosityData(parentLengths); Dictionary lengthsById = tile.Data(data).lengthsById; int riverId = tile.Data(rivers).riverId; if (!lengthsById.ContainsKey(riverId)) { lengthsById[riverId] = segmentLength; } else { lengthsById[riverId] += segmentLength; } foreach (Tile inlet in GetInlets(tile)) { MarkLengths(inlet, lengthsById, Rfloat2.Distance(tile.Center, inlet.Center)); } } private void MarkScores(Tile tile) { double num = tile.Data(data).score; num += config.sourceMult * ComputeSourceScore(tile); num += config.proximityMult * ComputeProximityScore(tile); num += config.angleMult * ComputeAngleScore(tile); tile.Data(data).score = num; foreach (Tile inlet in GetInlets(tile)) { MarkScores(inlet); } } private double ComputeSourceScore(Tile tile) { if (!tile.Data(rivers).IsRiverTermination()) { return 0.0; } return Rmath.InverseLerp(tile.Data(data).Length, config.sourceMinLength, config.sourceMaxLength); } private double ComputeProximityScore(Tile tile) { double num = 0.0; foreach (Tile item in tile.Scan(config.proximityScanRange)) { if (item.Data(data) != null) { double num2 = Rfloat2.Distance(tile.Center, item.Center); double num3 = GraphDistance(tile, item); double num4 = ((tile.Data(basins).basinId != item.Data(basins).basinId) ? config.proximityDifferentBasinsMultiplier : 1.0); double num5 = Rmath.InverseLerp(num3 / num2 * num4, config.proximityMinRatio, config.proximityMaxRatio); num += num5 * Functions.Gauss(num2 / config.proximityScanRange, 3.0); } } double num6 = commonConfig.tileSpacing / config.proximityScanRange; return num * num6 * num6; } private double ComputeAngleScore(Tile tile) { double num = 0.0; Direction direction = tile.DirectionTo(GetOutlet(tile)); foreach (Tile inlet in GetInlets(tile)) { double num2 = Rmath.Abs(tile.DirectionTo(inlet) - direction); double num3 = 1.0 - Rmath.InverseLerp(num2 * (180.0 / Math.PI), config.angleMinDegrees, config.angleMaxDegrees); num += num3 * (double)tile.Data(basins).width / config.angleNormWidth; } return num; } private void SmoothScores(Tile tile, MutableTileData result) { double smoothingRange = config.smoothingRange; InertFloat score = tile.Data(data).score; if ((double)score > 0.0) { ref InertFloat reference = ref tile.Data(result); reference = (double)reference + (double)score; foreach (Tile item in tile.Scan(smoothingRange)) { Rfloat2 rfloat = (tile.Center - item.Center) / smoothingRange; ref InertFloat reference2 = ref item.Data(result); reference2 = (double)reference2 + (double)score * Functions.Gauss2(rfloat.SqrLength, 3.0); } } foreach (Tile inlet in GetInlets(tile)) { SmoothScores(inlet, result); } } private double GraphDistance(Tile from, Tile to) { Dictionary dictionary = new Dictionary(from.Data(data).lengthsById); foreach (KeyValuePair item in to.Data(data).lengthsById) { if (!dictionary.ContainsKey(item.Key)) { dictionary[item.Key] = 0.0 - item.Value; } else { dictionary[item.Key] -= item.Value; } } double num = 0.0; foreach (double value in dictionary.Values) { num += Rmath.Abs(value); } return num; } private Tile GetOutlet(Tile tile) { return tiling.GetTile(tile.Data(rivers).outletId); } private IEnumerable GetInlets(Tile tile) { return tile.Data(rivers).inletIds.Select((int inletId) => tiling.GetTile(inletId)); } } public class RiverFillTile { public readonly Tile outlet; public readonly Couple inlets; public readonly InertFloat loss; public RiverFillTile(Tile outlet, Couple inlets, double loss) { this.outlet = outlet; this.inlets = inlets; this.loss = loss; } } [Producer(new string[] { "Rivers/FloodFill" })] [Fork("Region")] public class RiverFillStage : IGeneratorStage> { [Serializable] public struct Config { public double temperature; public double maxOriginRandomLoss; public RiverFillGuide.Config guide; } private class GraphNode { public readonly Tile outlet; public readonly TileList inlets; public readonly InertFloat loss; public GraphNode(Tile outlet, double loss) { this.outlet = outlet; this.loss = loss; inlets = new TileList(outlet.Tiling); } public RiverFillTile ToRiverFillTile() { return new RiverFillTile(outlet, Couple.MakeCouple(inlets.ToArray()), loss); } } public const string OUTPUT = "Rivers/FloodFill"; [Random("Rivers/FloodFill")] public RandomDirectory random; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Base")] public TileData flags; [Resource("LandmassIds")] public TileData landmassIds; [Resource("Landmasses/Artifacts")] public TileData artifacts; [Resource("Precipitation")] public TileData precipitationMap; [Resource("Rivers/Origins")] public List riverOrigins; private double Loss(TileData graph, Tile tile) { return (double)graph[tile].loss - (double)precipitationMap[tile]; } public TileData Compute() { MutableTileData mutableTileData = tiling.CreateData(); RiverFillGuide guide = new RiverFillGuide(random.Indexer("Guide"), config.guide); Dictionary dictionary = InitializeOrigins(mutableTileData); foreach (int key in dictionary.Keys) { ProcessLandmass(mutableTileData, guide, dictionary[key]); } return mutableTileData.Transform((GraphNode node) => node?.ToRiverFillTile()); } private Dictionary InitializeOrigins(MutableTileData graph) { RandomIndexer randomIndexer = random.Indexer("OriginRandomLoss"); Dictionary dictionary = new Dictionary(); foreach (RiverOrigin riverOrigin in riverOrigins) { int key = tiling.GetTile(riverOrigin.tileId).Data(landmassIds); if (!dictionary.ContainsKey(key)) { dictionary[key] = new TileList(tiling); } Tile tile = tiling.GetTile(riverOrigin.tileId); double loss = randomIndexer.Single(riverOrigin.tileId).Range(config.maxOriginRandomLoss); tile.Data(graph) = new GraphNode(tiling.GetTile(riverOrigin.seawardTileId), loss); dictionary[key].Add(tile); } return dictionary; } private void ProcessLandmass(MutableTileData graph, RiverFillGuide guide, TileList origins) { RandomTileHeap randomTileHeap = new RandomTileHeap(tiling, random.Indexer("TileSelection"), config.temperature); foreach (Tile origin in origins) { randomTileHeap.AddOrUpdate(origin, Loss(graph, origin)); } while (randomTileHeap.Count > 0) { Tile tile = randomTileHeap.Pop(); foreach (RiverFillGuide.Continuation item in SelectInlets(graph, guide, tile)) { graph[tile].inlets.Add(item.tile); graph[item.tile] = new GraphNode(tile, (double)graph[tile].loss + item.loss); randomTileHeap.AddOrUpdate(item.tile, Loss(graph, item.tile)); } } } private Couple SelectInlets(TileData graph, RiverFillGuide guide, Tile tile) { List list = new List(); foreach (Tile neighbor in tile.GetNeighbors()) { if (!(neighbor == tile.Data(graph).outlet) && RiverUtility.IsSuitableLand(flags[neighbor], artifacts[neighbor]) && neighbor.Data(graph) == null) { list.Add(neighbor); } } return guide.SelectConfiguration(tile, list, graph[tile].outlet); } } public class RiverFillGuide { [Serializable] public struct Config { public double temperature; public RiverFillGuideLossCalculator.Config loss; } public struct Continuation { public Tile tile; public double loss; } private class Configuration { public int id; public double loss; public Couple inlets; } private readonly RandomIndexer random; private readonly Config config; private readonly RiverFillGuideLossCalculator lossGuide; public RiverFillGuide(RandomIndexer random, Config config) { this.random = random; this.config = config; lossGuide = new RiverFillGuideLossCalculator(config.loss); } public Couple SelectConfiguration(Tile tile, List candidates, Tile outlet) { if (candidates.Count == 0) { return default(Couple); } IEnumerable candidates2 = from inlet in candidates.AllPairings() select MakeConfiguration(tile, inlet, outlet); return RandomSelect(candidates2).inlets; } private Configuration MakeConfiguration(Tile tile, Couple inlets, Tile outlet) { Direction outlet2 = outlet.DirectionTo(tile); Couple inlets2 = inlets.Transform((Tile inlet) => tile.DirectionTo(inlet)); Couple inletLosses; return new Configuration { id = ConfigurationId(tile, inlets), loss = lossGuide.ConfigurationLoss(inlets2, outlet2, out inletLosses), inlets = inlets.Transform(delegate(int i) { Continuation result = default(Continuation); result.tile = inlets[i]; result.loss = inletLosses[i]; return result; }) }; } private static int ConfigurationId(Tile origin, Couple inlets) { int id = origin.Id; int x = ((inlets.Count > 0) ? inlets[0].Id : (-1)); int y = ((inlets.Count > 1) ? inlets[1].Id : (-1)); return (int)HashTools.IntHash32((uint)id, x, y); } private Configuration RandomSelect(IEnumerable candidates) { Configuration result = null; double num = double.MinValue; foreach (Configuration candidate in candidates) { double num2 = random.Single(candidate.id).Gumbel(0.0 - candidate.loss, config.temperature); if (num2 > num) { num = num2; result = candidate; } } return result; } } public class RiverFillGuideLossCalculator { [Serializable] public struct Config { public double energyPenaltyPow; public double aestheticPenaltySteepness; public double haltConfigurationLoss; public double bendConfigurationLoss; public double bendDeflectionLoss; public double bendAestheticLoss; public double forkDeflectionLoss; public double forkImbalanceLoss; public double forkAestheticLoss; public double energyMult; public double continuationLoss; } private readonly Config config; public RiverFillGuideLossCalculator(Config config) { this.config = config; } public double ConfigurationLoss(Couple inlets, Direction outlet, out Couple inletLosses) { return inlets.Count switch { 0 => HaltLoss(out inletLosses), 1 => BendLoss(inlets[0], outlet, out inletLosses), 2 => ForkLoss(inlets[0], inlets[1], outlet, out inletLosses), _ => throw new IndexOutOfRangeException(), }; } private double HaltLoss(out Couple losses) { losses = default(Couple); return config.haltConfigurationLoss; } private double BendLoss(Direction inlet, Direction outlet, out Couple losses) { double num = config.bendDeflectionLoss * EnergyPenalty(inlet - outlet); double num2 = config.bendAestheticLoss * AestheticPenalty(inlet - outlet, Math.PI * 2.0 / 3.0); losses = new Couple(num2 + config.continuationLoss); return config.bendConfigurationLoss + num + num2; } private double ForkLoss(Direction inlet1, Direction inlet2, Direction outlet, out Couple losses) { double angle = outlet - inlet1; double angle2 = outlet - inlet2; double num = EnergyPenalty(angle); double num2 = EnergyPenalty(angle2); double num3 = num2 / (num + num2); double num4 = num / (num + num2); double num5 = ImbalanceLoss(Imbalance(num3, num4), config.forkImbalanceLoss); double angle3 = outlet - new Direction(num3 * inlet1.ToVector() + num4 * inlet2.ToVector()); double num6 = config.forkDeflectionLoss * EnergyPenalty(angle3); double num7 = AestheticPenalty(angle, Math.PI / 2.0); double num8 = AestheticPenalty(angle2, Math.PI / 2.0); double num9 = config.forkAestheticLoss * (num7 + num8); losses = new Couple(num7 + EnergyLoss(num3) + config.continuationLoss, num8 + EnergyLoss(num4) + config.continuationLoss); return num5 + num6 + num9; } private double EnergyPenalty(double angle) { return Rmath.Pow(2.0 * Rmath.Abs(angle) / Math.PI, config.energyPenaltyPow); } private double EnergyLoss(double weight) { return (0.0 - config.energyMult) * Rmath.Log(weight); } private double AestheticPenalty(double angle, double threshold) { return Rmath.Max(0.0, config.aestheticPenaltySteepness * (Rmath.Abs(angle) - threshold) + 1.0); } private static double Imbalance(double w1, double w2) { return 1.0 - 4.0 * w1 * w2; } private static double ImbalanceLoss(double imbalance, double multiplier) { return Rmath.Abs(multiplier) * ((multiplier > 0.0) ? imbalance : (1.0 - imbalance)); } } public class RiverMeanderer { [Serializable] public struct Config { public double period; public double riverAmplitude; public double channelAmplitude; } private readonly Config config; private double totalDistance; public RiverMeanderer(Config config) { this.config = config; totalDistance = 0.0; } public Rfloat2 Displacement(double step, double width, Rfloat2 tangent) { Rfloat2 result = Normal(tangent) * Displacement(totalDistance, width); totalDistance += step / width; return result; } private static Rfloat2 Normal(Rfloat2 tangent) { return new Rfloat2(0.0 - tangent.y, tangent.x).Normalized; } private double Displacement(double distance, double width) { double num = distance / config.period; return width * ((Rmath.Sin(Math.PI * num) + 0.5 * Rmath.Sin(2.0 * num)) / 1.5); } } public class RiverBasinTile { public int basinId; public int strahlerNumber; public InertFloat catchment; public InertFloat width; public Direction flowDirection; } public static class RiverBasinTileUtility { public const int NO_BASIN_ID = -1; public const double AVG_ANNUAL_DISCHARGE_CM = 230.0; public const double SECONDS_IN_YEAR = 31536000.0; public const double AVG_DISCHARGE_FLOW = 7.293252156265855E-08; public static double Discharge(this RiverBasinTile basinTile) { return (double)basinTile.catchment * 7.293252156265855E-08; } } [Serializable] public struct RiverWidthConfig { public double scale; public double power; public double offset; } [Producer(new string[] { "RiverTileMetrics" })] [Fork("Region")] public class RiverMetricMarker : IGeneratorStage> { public const string OUTPUT = "RiverTileMetrics"; [LaneConfig] public RiverWidthConfig config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("LandmassIds")] public TileData landmassIds; [Resource("Rivers/Origins")] public List riverOrigins; [Resource("Rivers/FloodFill")] public TileData riverFill; [Resource("Precipitation")] public TileData precipitation; public TileData Compute() { MutableTileData mutableTileData = tiling.CreateData(); foreach (Tile tile2 in tiling.Tiles) { tile2.Data(mutableTileData) = new RiverBasinTile { basinId = -1, catchment = (double)precipitation[tile2] * tile2.Area }; } HashSet hashSet = new HashSet(); foreach (RiverOrigin riverOrigin in riverOrigins) { int tileId = riverOrigin.tileId; Tile tile = tiling.GetTile(riverOrigin.tileId); hashSet.Add(landmassIds[tile]); MarkWeights(mutableTileData, tile, tileId); } MarkOrphanTiles(mutableTileData, hashSet); return mutableTileData; } private void MarkWeights(TileData metrics, Tile origin, int basinId) { TileList tileList = new TileList(tiling); TileQueue tileQueue = TileQueue.Of(origin); while (tileQueue.Count > 0) { Tile tile = tileQueue.Dequeue(); tileList.Add(tile); foreach (Tile inlet in riverFill[tile].inlets) { tileQueue.Enqueue(inlet); } } foreach (Tile item in tileList.Reversed()) { List list = new List(); InertFloat inertFloat = metrics[item].catchment; foreach (Tile inlet2 in riverFill[item].inlets) { list.Add(inlet2.Data(metrics).strahlerNumber); inertFloat = (double)inertFloat + (double)inlet2.Data(metrics).catchment; } metrics[item].basinId = basinId; metrics[item].strahlerNumber = ComputeStrahlerNumber(list); metrics[item].catchment = inertFloat; metrics[item].width = RiverWidth(metrics[item].Discharge()); metrics[item].flowDirection = item.DirectionTo(riverFill[item].outlet); } } private void MarkOrphanTiles(TileData metrics, HashSet basinLandmasses) { TileQueue tileQueue = new TileQueue(tiling); foreach (Tile tile2 in tiling.Tiles) { if (landmassIds[tile2] == -1 || !basinLandmasses.Contains(landmassIds[tile2]) || metrics[tile2].basinId != -1) { continue; } foreach (Tile neighbor in tile2.GetNeighbors()) { if (metrics[neighbor].basinId != -1) { tileQueue.Enqueue(neighbor); } } } while (tileQueue.Count > 0) { Tile tile = tileQueue.Dequeue(); foreach (Tile neighbor2 in tile.GetNeighbors()) { if (landmassIds[neighbor2] != -1 && metrics[neighbor2].basinId == -1) { metrics[neighbor2].basinId = metrics[tile].basinId; metrics[neighbor2].flowDirection = neighbor2.DirectionTo(tile); tileQueue.Enqueue(neighbor2); } } } } private static int ComputeStrahlerNumber(List upstreamNumbers) { if (upstreamNumbers.Count == 0) { return 1; } if (upstreamNumbers.Count == 1) { return upstreamNumbers[0]; } upstreamNumbers.Sort(); upstreamNumbers.Reverse(); if (upstreamNumbers[0] == upstreamNumbers[1]) { return upstreamNumbers[0] + 1; } return upstreamNumbers[0]; } private double RiverWidth(double discharge) { return Rmath.Max(0.0, config.scale * Rmath.Pow(discharge, config.power) + config.offset); } } public struct RiverOrigin { public int tileId; public int seawardTileId; } [Producer(new string[] { "Rivers/Origins" })] [Fork("Region")] public class RiverOriginFinder : IGeneratorStage> { [Serializable] public struct Config { public double density; public double minCoastality; public int minLandNeighbors; } public const string OUTPUT = "Rivers/Origins"; [Random("Rivers/Origins")] public RandomDirectory random; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Base")] public TileData flags; [Resource("Landmasses/Artifacts")] public TileData artifacts; public List Compute() { List list = new List(); if (config.density == 0.0) { return list; } RandomIndexer randomIndexer = random.Indexer("CoastalTileOrdering"); TileHeap tileHeap = new TileHeap(tiling, HeapType.Min); foreach (Tile tile2 in tiling.Tiles) { if (RiverUtility.IsSuitableCoast(flags[tile2], artifacts[tile2])) { tileHeap.AddOrUpdate(tile2, randomIndexer.Single(tile2.Id).Value); } } int num = Rmath.RoundToInt(config.density * (double)tileHeap.Count); while (list.Count < num) { if (tileHeap.Count == 0) { Logger.LogError($"Not enough coast to place {num} river origins, stopped abruptly at {list.Count}!"); break; } Tile tile = tileHeap.Pop(); if (tile.GetNeighbors().Count((Tile neighbor) => RiverUtility.IsSuitableLand(flags[neighbor], artifacts[neighbor])) < config.minLandNeighbors) { continue; } Rfloat2 zero = Rfloat2.Zero; foreach (Tile neighbor in tile.GetNeighbors()) { if (neighbor.Data(flags).IsOcean) { zero += (neighbor.Center - tile.Center).Normalized; } } if (!(zero.Length < config.minCoastality)) { list.Add(new RiverOrigin { tileId = tile.Id, seawardTileId = tile.GetNeighbor(new Direction(zero)).Id }); } } return list; } } public class RiverData { public delegate RiverVertexData VertexDataCorrection(RiverVertexData data, RiverVertexData upstreamData, double distance); public class Builder { private readonly List vertices = new List(); private readonly List data = new List(); private readonly List meander = new List(); private readonly Dictionary tributaries = new Dictionary(); public void AddVertex(int vertexId, double width, double depth, double steepness, double meanderAmplitude) { vertices.Add(vertexId); data.Add(new RiverVertexData { width = width, depth = depth, steepness = steepness }); meander.Add(meanderAmplitude); } public int GetLastVertexId() { return vertices.Last(); } public void AddTributary(int riverId, int vertexId) { tributaries[vertexId] = riverId; } private Tributary[] BuildTributaries() { List list = new List(); for (int i = 0; i < vertices.Count; i++) { int num = vertices[i]; if (tributaries.TryGetValue(num, out var value)) { list.Add(new Tributary(value, num, (double)i / (double)vertices.Count)); } } return list.ToArray(); } public void CorrectVertexData(VertexDataCorrection correction, List vertexPositions) { for (int num = vertices.Count; num > 0; num--) { if (num == vertices.Count) { data[num - 1] = correction(data[num - 1], default(RiverVertexData), double.MaxValue); } else { data[num - 1] = correction(data[num - 1], data[num], Rfloat2.Distance(vertexPositions[vertices[num]], vertexPositions[vertices[num - 1]])); } } } public RiverData Build() { return new RiverData { vertices = vertices, data = new PolylineData(data, RiverVertexData.Lerp), meander = new PolylineData(meander, Rmath.UnclampedLerp), tributaries = BuildTributaries() }; } } public readonly struct Tributary { public readonly int riverId; public readonly int vertexId; public readonly double progress; public Tributary(int riverId, int vertexId, double progress) { this.riverId = riverId; this.vertexId = vertexId; this.progress = progress; } } public List vertices = new List(); private PolylineData data; private PolylineData meander; private Tributary[] tributaries; public IEnumerable Vertices() { return vertices; } public PolylineData Data() { return data; } public PolylineData Meander() { return meander; } public IEnumerable Tributaries() { return tributaries; } } public class RiverPolylineIndex { public class Builder { private int maxRiverId = -1; private readonly List vertices = new List(); private readonly Dictionary rivers = new Dictionary(); private readonly List principals = new List(); public int AddVertex(Rfloat2 pos) { int count = vertices.Count; vertices.Add(pos); return count; } public RiverData.Builder AddRiver(int riverId, bool isPrincipal = false) { maxRiverId = Rmath.Max(maxRiverId, riverId); RiverData.Builder builder = new RiverData.Builder(); rivers[riverId] = builder; if (isPrincipal) { principals.Add(riverId); } return builder; } public RiverData.Builder AddRiver(bool isPrincipal = false) { return AddRiver(maxRiverId + 1, isPrincipal); } public RiverData.Builder GetRiver(int riverId) { return rivers[riverId]; } public void CorrectVertexData(RiverData.VertexDataCorrection correction) { foreach (RiverData.Builder value in rivers.Values) { value.CorrectVertexData(correction, vertices); } } public RiverPolylineIndex Build() { return new RiverPolylineIndex { vertices = vertices.ToArray(), rivers = rivers.ToDictionary((KeyValuePair kv) => kv.Key, (KeyValuePair kv) => kv.Value.Build()), principals = principals.ToArray() }; } } private InertFloat2[] vertices; private Dictionary rivers; private int[] principals; public IEnumerable Vertices() { return ((IEnumerable)vertices).Select((Func)((InertFloat2 pos) => pos)); } public RiverData GetRiverData(int riverId) { return rivers[riverId]; } public IEnumerable Principals() { return principals; } } [Producer(new string[] { "Rivers/PolylineIndex" })] [Fork("Region")] public class RiverPolylineIndexBuilder : IGeneratorStage { [Serializable] public struct Config { public double deepSeaExtension; public double oceanWidthMult; public double sourceMaxWidth; public double maxWidthChangePerMeter; public Rmath.RemapConfig heightToSteepness; } public const string OUTPUT = "Rivers/PolylineIndex"; [LaneConfig] public Config config; [LaneConfig] public RiverMeanderer.Config meanderConfig; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("PrunedRivers")] public TileData rivers; [Resource("Flags/Base")] public TileData flags; [Resource("RiverTileMetrics")] public TileData basins; [Resource("CoastalProximity")] public TileData coastalProximity; [Resource("Rivers/Channels")] public Channel[] channels; [Resource("Biomes/PostprocessedHeightMap")] public TileData heights; public RiverPolylineIndex Compute() { RiverPolylineIndex.Builder builder = new RiverPolylineIndex.Builder(); AddRivers(builder); builder.CorrectVertexData(delegate(RiverVertexData data, RiverVertexData upstreamData, double distance) { if ((double)upstreamData.width == 0.0) { data.width = Rmath.Min(data.width, config.sourceMaxWidth); return data; } if (config.maxWidthChangePerMeter == 0.0) { return data; } data.width = Rmath.Min(data.width, (double)upstreamData.width + distance * config.maxWidthChangePerMeter); return data; }); AddChannels(builder); return builder.Build(); } private void AddRivers(RiverPolylineIndex.Builder index) { Stack stack = new Stack(); foreach (Tile tile2 in tiling.Tiles) { if (!rivers[tile2].IsRiverMouth()) { continue; } stack.Push(tile2); RiverData.Builder builder = index.AddRiver(rivers[tile2].riverId, isPrincipal: true); foreach (Tile item in GetRiverPrefix(GetOutlet(tile2))) { builder.AddVertex(index.AddVertex(item.Center), (double)basins[tile2].width * config.oceanWidthMult, 1.0, Rmath.Remap(heights[item], config.heightToSteepness), 0.0); } } while (stack.Count > 0) { Tile tile = stack.Pop(); int riverId = rivers[tile].riverId; RiverData.Builder river = index.GetRiver(riverId); int vertexId = index.AddVertex(tile.Center); int lastVertexId = river.GetLastVertexId(); river.AddVertex(vertexId, basins[tile].width, (!rivers[tile].IsRiverTermination()) ? 1 : 0, Rmath.Remap(heights[tile], config.heightToSteepness), meanderConfig.riverAmplitude); foreach (Tile inlet in GetInlets(tile)) { stack.Push(inlet); int riverId2 = rivers[inlet].riverId; if (riverId2 != riverId) { river.AddTributary(riverId2, lastVertexId); RiverData.Builder builder2 = index.AddRiver(riverId2); builder2.AddVertex(lastVertexId, basins[inlet].width, 1.0, Rmath.Remap(heights[inlet], config.heightToSteepness), 0.0); builder2.AddVertex(vertexId, basins[inlet].width, (!rivers[tile].IsRiverTermination()) ? 1 : 0, Rmath.Remap(heights[inlet], config.heightToSteepness), meanderConfig.riverAmplitude); } } } } private void AddChannels(RiverPolylineIndex.Builder builder) { Channel[] array = channels; for (int i = 0; i < array.Length; i++) { Channel channel = array[i]; TileList tileList = channel.path; TileList tileList2 = FindOceanExtension(tileList.First()); if (tileList2 != null) { tileList = TilingUtility.JoinPaths(tileList2.Reversed(), tileList); } TileList tileList3 = FindOceanExtension(tileList.Last()); if (tileList3 != null) { tileList = TilingUtility.JoinPaths(tileList, tileList3); } RiverData.Builder builder2 = builder.AddRiver(isPrincipal: true); foreach (Tile item in tileList) { builder2.AddVertex(builder.AddVertex(item.Center), channel.width * (flags[item].IsOcean ? config.oceanWidthMult : 1.0), 1.0, Rmath.Remap(heights[item], config.heightToSteepness), meanderConfig.channelAmplitude); } } } private TileList GetRiverPrefix(Tile origin) { return FindOceanExtension(origin)?.Reversed() ?? TileList.Of(origin); } private TileList FindOceanExtension(Tile origin) { return TilingUtility.FindPathToValue(origin, config.deepSeaExtension, coastalProximity, (Tile tile) => tile.Data(flags).IsOcean); } private Tile GetOutlet(Tile tile) { return tiling.GetTile(tile.Data(rivers).outletId); } private IEnumerable GetInlets(Tile tile) { return tile.Data(rivers).inletIds.Select((int inletId) => tiling.GetTile(inletId)); } } [Producer(new string[] { "Rivers/Proximity" })] [Fork("Region")] public class RiverProximityStage : IGeneratorStage> { public const string OUTPUT = "Rivers/Proximity"; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("PrunedRivers")] public TileData rivers; public TileData Compute() { return tiling.DistanceTransform((Tile tile) => (!rivers[tile].IsRiver()) ? null : new double?(0.0), (Tile _) => true, (Tile t1, Tile t2) => Rfloat2.Distance(t1.Center, t2.Center)); } } public class RiverTile { public int riverId; public int outletId; public List inletIds; public bool isRiverMouth; } public static class RiverTileUtility { public static bool IsRiver(this RiverTile tile) { return tile != null; } public static bool IsRiverMouth(this RiverTile tile) { return tile?.isRiverMouth ?? false; } public static bool IsRiverFork(this RiverTile tile) { if (tile != null) { List inletIds = tile.inletIds; if (inletIds != null) { return inletIds.Count == 2; } } return false; } public static bool IsRiverTermination(this RiverTile tile) { if (tile != null) { List inletIds = tile.inletIds; if (inletIds != null) { return inletIds.Count == 0; } } return false; } public static bool IsOutletOf(this RiverTile tile, Tile other) { if (tile != null) { return tile.outletId == other.Id; } return false; } } [Producer(new string[] { "PrunedRivers" })] [Fork("Region")] public class RiverPruneStage : IGeneratorStage> { [Serializable] public struct Config { public int minStrahler; public double minWidth; public double maxWidthDiff; } public const string OUTPUT = "PrunedRivers"; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Rivers/Origins")] public List origins; [Resource("Rivers/FloodFill")] public TileData riverFill; [Resource("RiverTileMetrics")] public TileData basins; public TileData Compute() { MutableTileData mutableTileData = tiling.CreateData(); int maxRiverId = 0; foreach (RiverOrigin origin in origins) { Tile tile = tiling.GetTile(origin.tileId); if (MakePrunedRiver(mutableTileData, tile)) { tile.Data(mutableTileData).isRiverMouth = true; MarkRiverIds(mutableTileData, tile, ref maxRiverId); } } return mutableTileData; } private bool MakePrunedRiver(MutableTileData result, Tile start) { TileQueue tileQueue = TileQueue.Of(start); while (tileQueue.Count > 0) { Tile tile = tileQueue.Dequeue(); if (tile.Data(basins).strahlerNumber < config.minStrahler || (double)tile.Data(basins).width < config.minWidth) { continue; } int id = tile.Data(riverFill).outlet.Id; tile.Data(result) = new RiverTile { outletId = id, inletIds = new List() }; tiling.GetTile(id).Data(result)?.inletIds.Add(tile.Id); Couple inlets = tile.Data(riverFill).inlets; if (inlets.Count == 0) { continue; } double num = ((IEnumerable)tile.Data(riverFill).inlets).Max((Func)((Tile inlet) => inlet.Data(basins).width)); foreach (Tile item in inlets) { if (!(config.maxWidthDiff > 0.0) || !(num > config.maxWidthDiff * (double)item.Data(basins).width)) { tileQueue.Enqueue(item); } } } return start.Data(result).IsRiver(); } private void MarkRiverIds(TileData result, Tile tile, ref int maxRiverId) { if (tile.Data(result).IsRiverTermination()) { tile.Data(result).riverId = maxRiverId++; return; } double num = double.MinValue; int id = -1; foreach (int inletId in tile.Data(result).inletIds) { Tile tile2 = tiling.GetTile(inletId); MarkRiverIds(result, tile2, ref maxRiverId); if ((double)tile2.Data(basins).catchment > num) { num = tile2.Data(basins).catchment; id = inletId; } } tile.Data(result).riverId = tiling.GetTile(id).Data(result).riverId; } } public struct RiverVertexData { public InertFloat depth; public InertFloat width; public InertFloat steepness; public static RiverVertexData Lerp(double t, RiverVertexData lhs, RiverVertexData rhs) { RiverVertexData result = default(RiverVertexData); result.depth = Rmath.UnclampedLerp(t, lhs.depth, rhs.depth); result.width = Rmath.UnclampedLerp(t, lhs.width, rhs.width); result.steepness = Rmath.UnclampedLerp(t, lhs.steepness, rhs.steepness); return result; } } public struct River { public Spline spline; public PolylineData data; } [Producer(new string[] { "Rivers/Splines" })] [Fork("Region")] public class RiverSplineBuilder : IGeneratorStage { [Serializable] public struct Config { public enum SplineType { Hermite, BSpline } public double resamplingRate; public double widthVariationScale; public double widthVariability; public double meanderCurvatureMultiplier; public SplineType initialSplineType; public SplineType resampledSplineType; } private class ResampledRiver { public readonly List points = new List(); public readonly List data = new List(); public void Add(Rfloat2 point, RiverVertexData vertexData) { points.Add(point); data.Add(vertexData); } } public const string OUTPUT = "Rivers/Splines"; [Random("Rivers/SplineBuilder")] public RandomDirectory random; [LaneConfig] public Config config; [LaneConfig] public RiverMeanderer.Config meanderConfig; [Resource("Rivers/PolylineIndex")] public RiverPolylineIndex index; public River[] Compute() { List list = new List(); Rfloat2[] vertexIndex = index.Vertices().ToArray(); Queue queue = new Queue(); foreach (int item in index.Principals()) { queue.Enqueue(item); } NoiseField noise = config.widthVariability * random.Noise2D("Resampling/WidthWiggle", config.widthVariationScale); while (queue.Count > 0) { int riverId = queue.Dequeue(); foreach (RiverData.Tributary item2 in index.GetRiverData(riverId).Tributaries()) { queue.Enqueue(item2.riverId); } ResampledRiver input = Resample(riverId, vertexIndex, noise); list.Add(MakeRiver(riverId, input, vertexIndex)); } return list.ToArray(); } private ResampledRiver Resample(int riverId, Rfloat2[] vertexIndex, NoiseField noise) { double num = Rmath.Max(config.resamplingRate, 0.1); RiverData riverData = index.GetRiverData(riverId); Rfloat2[] points = riverData.vertices.Select((int id) => vertexIndex[id]).ToArray(); Spline spline = new Spline(CreateSplineImpl(config.initialSplineType, points)); RiverMeanderer riverMeanderer = new RiverMeanderer(meanderConfig); ResampledRiver resampledRiver = new ResampledRiver(); foreach (var item3 in spline.Walk()) { PolylineProgress item = item3.Item1; SplineWalk.StepEmitter item2 = item3.Item2; RiverVertexData vertexData = riverData.Data().Evaluate(item); Rfloat2 rfloat = spline.EvaluateTangent(item); double num2 = Rmath.Curvature(rfloat, spline.EvaluateAcceleration(item)); double step = num * (double)vertexData.width; item2.SetStep(step); Rfloat2 rfloat2 = riverData.Meander().Evaluate(item) * riverMeanderer.Displacement(step, vertexData.width, rfloat); if (num2 > 0.0 && config.meanderCurvatureMultiplier > 0.0) { rfloat2 /= Rmath.Max(1.0, config.meanderCurvatureMultiplier * rfloat2.Length * num2); } ref InertFloat width = ref vertexData.width; width = (double)width * (1.0 + noise.Sample(spline.EvaluatePosition(item))); resampledRiver.Add(spline.EvaluatePosition(item) + rfloat2, vertexData); } return resampledRiver; } private River MakeRiver(int riverId, ResampledRiver input, Rfloat2[] vertexIndex) { RiverData riverData = index.GetRiverData(riverId); Spline spline = new Spline(CreateSplineImpl(config.resampledSplineType, input.points)); RiverData.Tributary[] array = riverData.Tributaries().ToArray(); double num = 0.0; double[] array2 = new double[array.Length]; for (int i = 0; i < array.Length; i++) { array2[i] = array[i].progress * spline.Length - num; num += array2[i]; } int num2 = -1; foreach (var (pos, stepEmitter) in spline.Walk()) { if (num2 >= 0) { vertexIndex[array[num2].vertexId] = spline.EvaluatePosition(pos); } if (num2 == array.Length - 1) { break; } num2++; stepEmitter.SetStep(array2[num2]); } River result = default(River); result.spline = spline; result.data = new PolylineData(input.data, RiverVertexData.Lerp); return result; } private static ISplineImpl CreateSplineImpl(Config.SplineType splineType, IList points) { return splineType switch { Config.SplineType.Hermite => new HermiteSplineImpl(points, Tangents.FiniteDifference(points)), Config.SplineType.BSpline => new BSplineImpl(points), _ => throw new ArgumentOutOfRangeException("splineType", splineType, null), }; } } public static class RiverUtility { private static bool IsSuitableForRiver(TileFlags flags, LandmassArtifacts artifacts) { if (!flags.IsCenter) { return !artifacts.isFault; } return false; } public static bool IsSuitableLand(TileFlags flags, LandmassArtifacts artifacts) { if (IsSuitableForRiver(flags, artifacts)) { if (flags.IsLand) { return !flags.IsCoast; } return false; } return false; } public static bool IsSuitableCoast(TileFlags flags, LandmassArtifacts artifacts) { if (IsSuitableForRiver(flags, artifacts)) { if (flags.IsLand) { return flags.IsCoast; } return false; } return false; } } } namespace Riverheim.World.Landmass { public struct LandmassArtifacts { public bool isBasin; public bool isFault; public bool IsArtifact { get { if (!isBasin) { return isFault; } return true; } } } [Producer(new string[] { "Landmasses/Artifacts" })] [Fork("Region")] public class LandmassArtifactsCalculator : IGeneratorStage> { public const string OUTPUT = "Landmasses/Artifacts"; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Landmasses/Basins")] public TileSet basins; [Resource("Flags/Base")] public TileData flags; [Resource("Landmasses/MergedPartitions")] public TileData landmassPartitions; public TileData Compute() { return tiling.CreateData(delegate(Tile tile) { LandmassArtifacts result = default(LandmassArtifacts); result.isBasin = basins.Contains(tile); result.isFault = tile.GetNeighbors().Any(delegate(Tile neighbor) { int num = landmassPartitions[tile]; int num2 = landmassPartitions[neighbor]; return flags[tile].IsLand && flags[neighbor].IsLand && num != num2; }); return result; }); } } [Serializable] [CompositeConfig] public record LandmassConfig { public RegionalConfig restrictions; public RegionalConfig painter; public RegionalConfig postprocessing; public RegionalConfig partitionMerging; } [Serializable] public struct LandmassPainterConfig { public double temperature; public VectorNoiseField.RelativeWarpConfig layerWarp; public LayerPainter.Config layerPainter; } [Producer(new string[] { "LandmassLayers" })] [Fork("Region")] public class LandmassPainter : IGeneratorStage[]> { public const string OUTPUT = "LandmassLayers"; private const int NO_LANDMASS_ID = -1; private int currentId; [Lane] public Region region; [Random("LandmassPainter")] public RandomDirectory random; [LaneConfig] public LandmassPainterConfig config; [Resource("MainTiling")] public Tiling mainTiling; [Resource("TilingLods")] public Tiling[] tilingLods; [Resource("Regions/Tiling")] public Tiling regionTiling; [Resource("Regions")] public TileData[] regions; [Resource("LandmassRestrictions")] public LandmassRestrictions restrictions; public TileData[] Compute() { List list = CreateLayers(); LayerPainter layerPainter = CreateLayerPainter(); for (int num = list.Count - 1; num >= 0; num--) { Tiling tiling = list[num].tiling; MutableTileData landmassIds = list[num].landmassIds; double inheritedArea = 0.0; if (num < list.Count - 1) { InheritLayer(list[num], list[num + 1], ref inheritedArea); } layerPainter.PaintLayer(list[num], ref currentId, inheritedArea); Tile? tile = tiling.GetTile(Rfloat2.Zero); if (tile.HasValue) { Tile valueOrDefault = tile.GetValueOrDefault(); if (landmassIds[valueOrDefault] == -1) { TryCorrectCenter(valueOrDefault, landmassIds); } } } return ((IEnumerable)list).Select((Func>)((LayerPainter.Layer layer) => layer.landmassIds)).ToArray(); } private List CreateLayers() { List list = new List(); list.Add(CreateLayer(0, mainTiling)); list.AddRange(tilingLods.Select((Tiling tiling, int lod) => CreateLayer(lod + 1, tiling))); return list; } private LayerPainter.Layer CreateLayer(int lod, Tiling tiling) { return new LayerPainter.Layer(lod, tiling, random.Sub($"Layer/{lod}")); } private LayerPainter CreateLayerPainter() { return new LayerPainter(region, random.Sub("LayerPainter"), config.layerPainter, restrictions, regionTiling.TotalArea, config.temperature); } private void TryCorrectCenter(Tile center, MutableTileData landmassIds) { foreach (Tile neighbor in center.GetNeighbors()) { if (landmassIds[neighbor] != -1) { landmassIds[center] = landmassIds[neighbor]; } } if (landmassIds[center] == -1) { landmassIds[center] = currentId++; } } private void InheritLayer(LayerPainter.Layer to, LayerPainter.Layer from, ref double inheritedArea) { VectorNoiseField vectorNoiseField = random.WarpNoise("LayerWarp", to.tiling.Spacing, config.layerWarp); foreach (Tile tile2 in to.tiling.Tiles) { if (regions[to.lod][tile2] != region || restrictions.IsTileRestricted(tile2, to.lod)) { continue; } Tile? tile = from.tiling.GetTile(tile2.Center + vectorNoiseField.Sample(tile2.Center)); if (!tile.HasValue) { continue; } Tile valueOrDefault = tile.GetValueOrDefault(); if (regions[from.lod][valueOrDefault] == region) { to.landmassIds[tile2] = from.landmassIds[valueOrDefault]; if (to.landmassIds[tile2] != -1) { inheritedArea += tile2.Area; } } } } } [Producer(new string[] { "Landmasses/Distances/LocalMaxima", "Landmasses/Partitions" })] [Fork("Region")] public class LandmassPartitioner : IGeneratorStage<(TileList, TileData)> { public const string MAXIMA = "Landmasses/Distances/LocalMaxima"; public const string OUTPUT = "Landmasses/Partitions"; private const int OCEAN = -1; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Base")] public TileData flags; [Resource("CoastalProximity")] public TileData coastalProximity; public (TileList, TileData) Compute() { TileList localMaxima = GetLocalMaxima(); TileData partitionsByWatershed = GetPartitionsByWatershed(localMaxima); return (localMaxima, partitionsByWatershed); } private TileData GetPartitionsByWatershed(TileList localMaxima) { MutableTileData mutableTileData = tiling.CreateData(-1); TileHeap tileHeap = new TileHeap(tiling, HeapType.Max); for (int i = 0; i < localMaxima.Count; i++) { Tile tile = localMaxima[i]; mutableTileData[tile] = i; tileHeap.AddOrUpdate(tile, coastalProximity[tile]); } while (tileHeap.Count > 0) { Tile tile2 = tileHeap.Pop(); foreach (Tile neighbor in tile2.GetNeighbors()) { if (neighbor.Data(flags).IsLand && neighbor.Data(mutableTileData) == -1 && !tileHeap.Contains(neighbor)) { mutableTileData[neighbor] = mutableTileData[tile2]; tileHeap.AddOrUpdate(neighbor, coastalProximity[neighbor]); } } } return mutableTileData; } private TileList GetLocalMaxima() { TileList tileList = new TileList(tiling); foreach (Tile tile in tiling.Tiles) { TileFlags tileFlags = tile.Data(flags); if (tileFlags.IsLand && !tileFlags.IsCoast) { InertFloat distance = tile.Data(coastalProximity); if (!tile.GetNeighbors().Any((Tile neighbor) => neighbor.Data(flags).IsLand && (double)distance < (double)neighbor.Data(coastalProximity))) { tileList.Add(tile); } } } return tileList; } } [Producer(new string[] { "Landmasses/MergedPartitions" })] [Fork("Region")] public class LandmassPartitionMerger : IGeneratorStage> { [Serializable] public struct Config { public double mergeThreshold; public double coastAffinity; public double neighborAffinity; public Rmath.RemapConfig sizeAffinity; public Rmath.RemapConfig borderLengthAffinity; } private struct PartitionInfo { public Tile origin; public TileSet tiles; public Dictionary border; public double MergePriority => tiles.Count; public double NeighborAffinity(int neighborId) { TileSet value; double num = (border.TryGetValue(neighborId, out value) ? value.Count : 0); double num2 = border.Sum((KeyValuePair pair) => pair.Value.Count); return num / num2; } } public const string OUTPUT = "Landmasses/MergedPartitions"; private const int OCEAN = -1; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Landmasses/Distances/LocalMaxima")] public TileList localMaxima; [Resource("Landmasses/Partitions")] public TileData partitions; [Resource("CoastalProximity")] public TileData coastalProximity; public TileData Compute() { Dictionary dictionary = ComputePartitionInfos(); IntHeap intHeap = new IntHeap(HeapType.Min); foreach (int key in dictionary.Keys) { intHeap.AddOrUpdate(key, dictionary[key].MergePriority); } while (intHeap.Count > 0) { int num = intHeap.Pop(); int num2 = -1; double num3 = double.MinValue; foreach (int key2 in dictionary[num].border.Keys) { if (intHeap.Contains(key2)) { double num4 = AffinityScore(num, key2, dictionary); if (num4 > num3) { num3 = num4; num2 = key2; } } } if (num3 < config.mergeThreshold) { continue; } DoMerge(num, num2, dictionary); intHeap.Remove(num); intHeap.AddOrUpdate(num2, dictionary[num2].MergePriority); foreach (int key3 in dictionary[num2].border.Keys) { if (key3 != -1) { intHeap.AddOrUpdate(key3, dictionary[key3].MergePriority); } } } MutableTileData mutableTileData = tiling.CreateData(-1); foreach (int key4 in dictionary.Keys) { foreach (Tile item in dictionary[key4].tiles.Unordered()) { mutableTileData[item] = key4; } } return mutableTileData; } private Dictionary ComputePartitionInfos() { Dictionary dictionary = new Dictionary(); foreach (int item in Enumerable.Range(0, localMaxima.Count)) { dictionary.Add(item, new PartitionInfo { origin = localMaxima[item], tiles = new TileSet(tiling), border = new Dictionary() }); } foreach (Tile tile in tiling.Tiles) { int num = partitions[tile]; if (num == -1) { continue; } PartitionInfo partitionInfo = dictionary[num]; partitionInfo.tiles.Add(tile); foreach (Tile neighbor in tile.GetNeighbors()) { int num2 = partitions[neighbor]; if (num2 != num) { if (!partitionInfo.border.ContainsKey(num2)) { partitionInfo.border[num2] = new TileSet(tiling); } partitionInfo.border[num2].Add(tile); } } } return dictionary; } private double AffinityScore(int fromId, int intoId, Dictionary partitionInfos) { PartitionInfo partitionInfo = partitionInfos[fromId]; PartitionInfo partitionInfo2 = partitionInfos[intoId]; double value = tiling.Spacing * Rmath.Sqrt(Rmath.Sqrt(partitionInfo.tiles.Count) * Rmath.Sqrt(partitionInfo2.tiles.Count)); return 0.0 + config.neighborAffinity * Rmath.Max(partitionInfo.NeighborAffinity(intoId), partitionInfo2.NeighborAffinity(fromId)) + config.coastAffinity * (1.0 - Rmath.Min(partitionInfo.NeighborAffinity(-1), partitionInfo2.NeighborAffinity(-1))) + Rmath.Remap(value, config.sizeAffinity) + Rmath.Remap(tiling.Spacing * (double)partitionInfo.border[intoId].Count, config.borderLengthAffinity); } private void DoMerge(int fromId, int intoId, Dictionary partitionInfos) { PartitionInfo partitionInfo = partitionInfos[fromId]; PartitionInfo partitionInfo2 = partitionInfos[intoId]; foreach (int key in partitionInfo.border.Keys) { if (key == intoId) { continue; } if (!partitionInfo2.border.ContainsKey(key)) { partitionInfo2.border[key] = new TileSet(tiling); } foreach (Tile item in partitionInfo.border[key].Unordered()) { partitionInfo2.border[key].Add(item); } if (key == -1) { continue; } PartitionInfo partitionInfo3 = partitionInfos[key]; if (!partitionInfo3.border.ContainsKey(intoId)) { partitionInfo3.border[intoId] = new TileSet(tiling); } foreach (Tile item2 in partitionInfo3.border[fromId].Unordered()) { partitionInfo3.border[intoId].Add(item2); } partitionInfo3.border.Remove(fromId); } partitionInfo2.border.Remove(fromId); foreach (Tile item3 in partitionInfo.tiles.Unordered()) { partitionInfo2.tiles.Add(item3); } if ((double)partitionInfo.origin.Data(coastalProximity) > (double)partitionInfo2.origin.Data(coastalProximity)) { partitionInfo2.origin = partitionInfo.origin; } partitionInfos.Remove(fromId); } } [Serializable] public struct LandmassPostprocessingConfig { public double coastalTrimNoiseScale; public double coastalTrimDistance; public double coastalTrimVariance; public double effectiveSpawnRadius; public double smallIslandSizeThreshold; } [Producer(new string[] { "LandmassIds", "Landmasses/Basins" })] [Fork("Region")] public class LandmassPostprocessor : IGeneratorStage<(TileData, TileSet)> { public const string LANDMASS_IDS = "LandmassIds"; public const string BASINS = "Landmasses/Basins"; [Random("LandmassPostprocessor")] public RandomDirectory random; [LaneConfig] public LandmassPostprocessingConfig config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Regions/Mapping")] public SubtilingMapping mapping; [Resource("LandmassLayers")] public TileData[] layeredLandmassIds; public (TileData, TileSet) Compute() { MutableTileData land = tiling.CreateData((Tile tile) => layeredLandmassIds[0][mapping.GetParentTile(tile)] != -1); NoiseField noise = random.Noise2D("CoastalTrim", config.coastalTrimNoiseScale); double minArea = Math.PI * config.effectiveSpawnRadius * config.effectiveSpawnRadius; LandmassUtility.TrimCoastline(tiling, land, noise, config.coastalTrimDistance, config.coastalTrimVariance); LandmassUtility.Trim(tiling, land); LandmassUtility.RemoveLakes(tiling, land, out var removed); LandmassUtility.EnsureStartingAreaIsLand(tiling, land, minArea); MutableTileData mutableTileData = tiling.MarkConnectedComponents((Tile tile) => land[tile]).Copy(); double minArea2 = config.smallIslandSizeThreshold * config.smallIslandSizeThreshold; LandmassUtility.CullSmallIslandsInRegion(tiling, mutableTileData, (Tile _) => true, minArea2); return (mutableTileData, removed); } } [Serializable] public struct LandmassRestrictionConfig { public double worldEdgeWidth; public double polarGapWidth; public double reach; public SwirlConfig swirl; public HarborConfig harbor; public VectorNoiseField.AbsoluteWarpConfig featureWarp; } [Serializable] public struct SwirlConfig { public int rays; public double exponent; public double startRadius; public double noiseScale; public double noiseOffset; public double toleranceMult; } [Serializable] public struct HarborConfig { public double offset; public double radius; public double circularity; } [Producer(new string[] { "LandmassRestrictions" })] [Fork("Region")] public class LandmassRestrictionStage : IGeneratorStage { private class GeometricRestrictions { private readonly double worldSize; private readonly LandmassRestrictionConfig config; private readonly Rfloat2 midpoint; private readonly double swirlChirality; private readonly Direction swirlRotation; private readonly NoiseField swirlNoise; private readonly Direction harborRotation; public GeometricRestrictions(RandomDirectory random, double worldSize, LandmassRestrictionConfig config, Rfloat2 midpoint) { this.worldSize = worldSize; this.config = config; this.midpoint = midpoint; swirlChirality = Rmath.Sign(random.Single("Swirl/Chirality").Value - 0.5); swirlRotation = new Direction(random.Single("Swirl/Rotation").Range(Math.PI * 2.0)); swirlNoise = random.Noise2D("Swirl/Noise", config.swirl.noiseScale) + config.swirl.noiseOffset; harborRotation = new Direction(random.Single("Harbor/Rotation").Range(Math.PI * 2.0)); } public bool IsInHarbor(Rfloat2 pos) { double offset = config.harbor.offset; double radius = config.harbor.radius; double angle = harborRotation.Angle; if (radius <= 0.0) { return false; } Rfloat2 rfloat = pos - (offset + radius) * harborRotation.ToVector(); return rfloat.SqrLength <= HarborFunc(rfloat.x, rfloat.y, angle); } private double HarborFunc(double x, double y, double angle) { double radius = config.harbor.radius; double circularity = config.harbor.circularity; return radius * radius * BellCurve((x * Rmath.Sin(angle) - y * Rmath.Cos(angle)) / (radius * circularity)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double BellCurve(double x) { return 1.0 / (1.0 + x * x); } public bool IsInSwirl(Rfloat2 pos) { if (config.swirl.rays <= 0) { return false; } double length = pos.Length; double num = swirlChirality * (new Direction(pos) - swirlRotation); if (length < config.swirl.startRadius) { return false; } double num2 = config.swirl.toleranceMult * swirlNoise.Sample(pos); for (int i = 0; i <= 10; i++) { for (double num3 = 0.0; num3 < (double)config.swirl.rays; num3 += 1.0) { double num4 = Math.PI * 2.0 * ((double)i + num3 / (double)config.swirl.rays); double num5 = Rmath.Pow(config.swirl.exponent, num + num4); if (Rmath.Abs(length - num5) < num5 * num2) { return true; } if (num5 > worldSize) { return false; } } } return false; } public bool OutOfRange(Rfloat2 pos) { return (midpoint - pos).Length > config.reach; } public bool IsWorldEdge(Rfloat2 pos) { return pos.Length > worldSize - config.worldEdgeWidth; } } public const string OUTPUT = "LandmassRestrictions"; [Lane] public Region region; [Random("LandmassRestrictions")] public RandomDirectory random; [Config] public CommonConfig commonConfig; [Config] public PolarBeltConfig polarBeltConfig; [LaneConfig] public LandmassRestrictionConfig config; [Resource("PolarProximity")] public PolarProximityMap polarProximity; [Resource("MainTiling")] public Tiling tiling; [Resource("TilingLods")] public Tiling[] tilingLods; [Resource("Regions")] public TileData[] regions; public LandmassRestrictions Compute() { GeometricRestrictions restrictions = new GeometricRestrictions(random.Sub("Geometric"), commonConfig.worldSize, config, RegionMidpoint()); TileSet[] array = new TileSet[tilingLods.Length + 1]; for (int i = 0; i < array.Length; i++) { array[i] = new TileSet(tiling); } VectorNoiseField featureWarp = random.WarpNoise("FeatureWarp", config.featureWarp); foreach (Tile tile2 in tiling.Tiles) { if (!HasGeometricRestriction(tile2, restrictions, featureWarp)) { continue; } array[0].Add(tile2); for (int j = 0; j < tilingLods.Length; j++) { Tile? tile = tilingLods[j].GetTile(tile2.Center); if (tile.HasValue) { Tile valueOrDefault = tile.GetValueOrDefault(); array[j + 1].Add(valueOrDefault); } } } return new LandmassRestrictions(region, regions, array); } private Rfloat2 RegionMidpoint() { double num = Rmath.Lerp(0.5, commonConfig.worldSize, polarBeltConfig.radius - polarBeltConfig.offset); return region switch { Region.Main => Rfloat2.Zero, Region.NorthPole => new Rfloat2(0.0, num), Region.SouthPole => new Rfloat2(0.0, 0.0 - num), _ => throw new ArgumentOutOfRangeException("region", region, null), }; } private bool HasGeometricRestriction(Tile tile, GeometricRestrictions restrictions, VectorNoiseField featureWarp) { Rfloat2 center = tile.Center; if (restrictions.IsWorldEdge(center)) { return true; } if (Rmath.Abs(polarProximity(center)) <= config.polarGapWidth) { return true; } Rfloat2 pos = center + featureWarp.Sample(center); if (restrictions.IsInHarbor(pos)) { return true; } if (restrictions.IsInSwirl(pos)) { return true; } if (restrictions.OutOfRange(pos)) { return true; } return false; } } public readonly struct LandmassRestrictions { private readonly Region region; private readonly TileData[] regions; private readonly TileSet[] restrictedTiles; public LandmassRestrictions(Region region, TileData[] regions, TileSet[] restrictedTiles) { this.region = region; this.regions = regions; this.restrictedTiles = restrictedTiles; } public bool IsTileRestricted(Tile tile, int lod) { if (tile.HopsToBounds == 0) { return true; } if (region != regions[lod][tile]) { return true; } if (restrictedTiles[lod].Contains(tile)) { return true; } return false; } } public static class LandmassUtility { public static double CalcLandmassArea(Tile origin, Predicate isLand) { double num = 0.0; if (!isLand(origin)) { return num; } TileSet tileSet = new TileSet(origin.Tiling); TileQueue tileQueue = new TileQueue(origin.Tiling); tileSet.Add(origin); tileQueue.Enqueue(origin); while (tileQueue.Count > 0) { Tile tile = tileQueue.Dequeue(); num += tile.Area; foreach (Tile neighbor in tile.GetNeighbors()) { if (isLand(neighbor) && tileSet.Add(neighbor)) { tileQueue.Enqueue(neighbor); } } } return num; } public static void Trim(Tiling tiling, MutableTileData land, int distance = 1) { foreach (Tile tile in tiling.Tiles) { if (tile.HopsToBounds < distance) { tile.Data(land) = false; } } } public static void TrimCoastline(Tiling tiling, MutableTileData land, NoiseField noise, double threshold, double variance) { TileData data = tiling.DistanceTransform(delegate(Tile tile) { foreach (Tile neighbor in tile.GetNeighbors()) { if (!neighbor.Data(land)) { return 0.5 * tiling.Spacing; } } return null; }, (Tile tile) => tile.Data(land)); foreach (Tile tile in tiling.Tiles) { if ((double)tile.Data(data) < threshold * (1.0 + variance * noise.Sample(tile.Center))) { tile.Data(land) = false; } } } public static void EnsureStartingAreaIsLand(Tiling tiling, MutableTileData land, double minArea = 0.0) { Tile? tile = tiling.GetTile(Rfloat2.Zero); if (!tile.HasValue) { return; } Tile valueOrDefault = tile.GetValueOrDefault(); valueOrDefault.Data(land) = true; int num = 1; while (CalcLandmassArea(valueOrDefault, (Tile t) => t.Data(land)) < minArea) { foreach (Tile neighbor in valueOrDefault.GetNeighbors(num++)) { neighbor.Data(land) = true; } } } public static void RemoveLakes(Tiling tiling, MutableTileData land, out TileSet removed) { TileQueue tileQueue = new TileQueue(tiling); TileSet tileSet = new TileSet(tiling); foreach (Tile tile in tiling.Tiles) { if (!tile.Data(land)) { if (tile.HopsToBounds == 0) { tileQueue.Enqueue(tile); } else { tileSet.Add(tile); } } } if (tiling.Count > 0 && tileQueue.Count == 0) { Logger.LogError("landmass doesn't contain any ocean at bounds!"); } while (tileQueue.Count > 0) { foreach (Tile neighbor in tileQueue.Dequeue().GetNeighbors()) { if (tileSet.Contains(neighbor)) { tileSet.Remove(neighbor); tileQueue.Enqueue(neighbor); } } } removed = tileSet; foreach (Tile item in removed.ConsistentOrdering()) { item.Data(land) = true; } } public static void CullSmallIslandsInRegion(Tiling tiling, MutableTileData landmassIds, Predicate isInRegion, double minArea) { HashSet hashSet = new HashSet(); HashSet hashSet2 = new HashSet(); foreach (Tile tile in tiling.Tiles) { int landmassId = tile.Data(landmassIds); if (isInRegion(tile) && landmassId != -1 && hashSet.Add(landmassId) && CalcLandmassArea(tile, (Tile t) => t.Data(landmassIds) == landmassId) < minArea) { hashSet2.Add(landmassId); } } foreach (Tile tile2 in tiling.Tiles) { if (hashSet2.Contains(tile2.Data(landmassIds))) { tile2.Data(landmassIds) = -1; } } } } [Serializable] public struct LandmassLayerConfig { public double budget; public double greediness; public double variance; } public class LayerPainter { [Serializable] public struct Config { public double distanceWeighting; public double cohesionWeighting; public StaticArray layers; } public struct Layer { public readonly int lod; public readonly Tiling tiling; public readonly MutableTileData landmassIds; public readonly RandomIndexer orderingRandom; public readonly RandomIndexer weighingRandom; public Layer(int lod, Tiling tiling, RandomDirectory random) { this.lod = lod; this.tiling = tiling; landmassIds = tiling.CreateData(-1); orderingRandom = random.Indexer("Ordering"); weighingRandom = random.Indexer("Weighing"); } } private const int NO_LANDMASS_ID = -1; public readonly Region region; private readonly RandomDirectory random; private readonly Config config; private readonly LandmassRestrictions restrictions; private readonly double regionArea; private readonly double temperature; public LayerPainter(Region region, RandomDirectory random, Config config, LandmassRestrictions restrictions, double regionArea, double temperature) { this.region = region; this.random = random; this.config = config; this.restrictions = restrictions; this.regionArea = regionArea; this.temperature = temperature; } public void PaintLayer(Layer layer, ref int currentLandmassId, double existingArea) { if (layer.lod >= config.layers.count) { return; } LandmassLayerConfig landmassLayerConfig = config.layers[layer.lod]; double num = GetTotalBudget(layer) - existingArea; RandomIndexer randomIndexer = random.Indexer("BudgetVariance/" + layer.lod); TileHeap tileHeap = MakeAvailableTiles(layer); while (num > 0.0 && tileHeap.Count > 0) { Tile origin = tileHeap.Pop(); double num2 = num * landmassLayerConfig.greediness * (1.0 + (double)randomIndexer.Single(origin.Id).Range(-1, 1) * landmassLayerConfig.variance); if (num2 <= 0.0) { throw new InvalidOperationException("landmass budget is negative"); } foreach (Tile item in PaintLandmass(layer, origin, num2, tileHeap).ConsistentOrdering()) { item.Data(layer.landmassIds) = currentLandmassId; tileHeap.Remove(item); foreach (Tile neighbor in item.GetNeighbors()) { tileHeap.Remove(neighbor); } num -= item.Area; } currentLandmassId++; } } private TileHeap MakeAvailableTiles(Layer layer) { TileHeap tileHeap = new TileHeap(layer.tiling, HeapType.Min); foreach (Tile item in AvailableTileSet(layer.lod, layer.tiling, layer.landmassIds).Unordered()) { tileHeap.AddOrUpdate(item, layer.orderingRandom.Single(item.Id).Value); } return tileHeap; } private TileSet AvailableTileSet(int lod, Tiling tiling, TileData landmassIds) { TileSet tileSet = TileSet.All(tiling); foreach (Tile tile in tiling.Tiles) { if (restrictions.IsTileRestricted(tile, lod)) { tileSet.Remove(tile); } if (tile.Data(landmassIds) == -1) { continue; } tileSet.Remove(tile); foreach (Tile neighbor in tile.GetNeighbors()) { tileSet.Remove(neighbor); } } return tileSet; } private double GetTotalBudget(Layer layer) { double num = 0.0; for (int num2 = config.layers.count - 1; num2 >= layer.lod; num2--) { num += config.layers[num2].budget; } return regionArea * num; } private TileSet PaintLandmass(Layer layer, Tile origin, double budget, TileHeap available) { TileSet tileSet = new TileSet(origin.Tiling); Dictionary dictionary = new Dictionary(); RandomTileHeap randomTileHeap = new RandomTileHeap(layer.tiling, layer.weighingRandom, temperature); randomTileHeap.AddOrUpdate(origin, 0.0); while (randomTileHeap.Count > 0 && budget > 0.0) { Tile tile = randomTileHeap.Pop(); budget -= tile.Area; tileSet.Add(tile); foreach (Tile neighbor in tile.GetNeighbors()) { if (available.Contains(neighbor) && !tileSet.Contains(neighbor)) { int valueOrDefault = dictionary.GetValueOrDefault(neighbor.Id, 0); randomTileHeap.AddOrUpdate(neighbor, Loss(origin, neighbor, valueOrDefault)); dictionary[neighbor.Id] = valueOrDefault + 1; } } } return tileSet; } private double Loss(Tile origin, Tile tile, int cohesion) { return Rmath.Log((1.0 + config.distanceWeighting * (origin.Center - tile.Center).Length / origin.Tiling.Spacing) / (1.0 + (double)cohesion * config.cohesionWeighting)); } } } namespace Riverheim.World.Height { [Producer(new string[] { "Heights/DifferentialHeightMap" })] [Fork("Region")] public class DifferentialHeightStage : IGeneratorStage> { [Serializable] public struct Config { public double startingAreaBonus; public DifferentialInclineCalculator.Config land; public DifferentialInclineCalculator.Config water; } public const string OUTPUT = "Heights/DifferentialHeightMap"; [Random("Heights/IntegralHeightMap")] public RandomDirectory random; [Config] public CommonConfig commonConfig; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Heights/DifferentialHeightBoundary")] public TileData boundary; [Resource("Flags/Extended")] public TileData flags; [Resource("PrunedRivers")] public TileData riverGraph; [Resource("RiverTileMetrics")] public TileData basins; [Resource("Landmasses/MergedPartitions")] public TileData landmassPartitions; [Resource("Heights/Flatlands")] public TileData flatlands; [Resource("TravelDistance")] public TileData travelDistance; public TileData Compute() { MutableTileData mutableTileData = boundary.Copy(); double startingAreaRadius = commonConfig.startingAreaRadius * (1.0 + config.startingAreaBonus); Compute(mutableTileData, (Tile tile) => flags[tile].IsLand, new DifferentialInclineCalculator(random.Sub("Incline/Land"), config.land, (Tile tile) => (double)travelDistance[tile] < startingAreaRadius || flatlands[tile], landmassPartitions, riverGraph, basins)); Compute(mutableTileData, (Tile tile) => flags[tile].IsWater, new DifferentialInclineCalculator(random.Sub("Incline/Water"), config.water, (Tile _) => false, landmassPartitions, riverGraph, basins)); return mutableTileData; } private void Compute(MutableTileData result, Predicate isReachable, DifferentialInclineCalculator calculator) { TileHeap tileHeap = new TileHeap(tiling, HeapType.Min); TileSet tileSet = new TileSet(tiling); foreach (Tile tile2 in tiling.Tiles) { if (isReachable(tile2) && (double)result[tile2] < double.MaxValue) { tileHeap.AddOrUpdate(tile2, result[tile2]); } } while (tileHeap.Count > 0) { double weight; Tile tile = tileHeap.Pop(out weight); result[tile] = weight; tileSet.Add(tile); foreach (Tile neighbor in tile.GetNeighbors()) { if (isReachable(neighbor) && !tileSet.Contains(neighbor)) { double num = calculator.Incline(tile, neighbor) * Rfloat2.Distance(tile.Center, neighbor.Center); if (!tileHeap.Contains(neighbor, out var weight2) || weight2 > weight + num) { tileHeap.AddOrUpdate(neighbor, weight + num); } } } } } } [Producer(new string[] { "Heights/DifferentialHeightBoundary" })] [Fork("Region")] public class DifferentialHeightBoundaryStage : IGeneratorStage> { [Serializable] public struct Config { public double coastalBonus; public double startingArea; public double fault; public Rmath.RemapConfig river; public double coastalNoiseScale; public Rmath.RemapConfig coastalLandHeight; public Rmath.RemapConfig coastalWaterDepth; public double riverDeltaDepth; } public const string OUTPUT = "Heights/DifferentialHeightBoundary"; [Random("Heights/DifferentialHeightBoundary")] public RandomDirectory random; [Config] public CommonConfig commonConfig; [LaneConfig] public Config distanceBasedConfig; [LaneConfig] public HeightShapingStage.Config shapingConfig; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Extended")] public TileData flags; [Resource("RiverTileMetrics")] public TileData basins; [Resource("PrunedRivers")] public TileData rivers; [Resource("Landmasses/Artifacts")] public TileData artifacts; public TileData Compute() { MutableTileData mutableTileData = tiling.CreateData((InertFloat)double.MaxValue); TileData tileData = RiverMouthProximity(); Config config = ConvertHeightBasedConfigToDistanceBased(); NoiseField noiseField = random.BiasedNoise2D("CoastalDisplacement", config.coastalNoiseScale); foreach (Tile tile in tiling.Tiles) { if (flags[tile].IsLand) { if (!config.river.IsEmpty && rivers[tile].IsRiver()) { mutableTileData[tile] = Rmath.Min(mutableTileData[tile], Rmath.Remap(basins[tile].width, config.river)); } if (config.startingArea > 0.0 && tile.Center.Length < commonConfig.startingAreaRadius) { mutableTileData[tile] = Rmath.Min(mutableTileData[tile], config.startingArea); } if (config.fault > 0.0 && artifacts[tile].isFault) { mutableTileData[tile] = Rmath.Min(mutableTileData[tile], config.fault); } } if (!flags[tile].IsCoast) { continue; } double num = 0.5 * commonConfig.tileSpacing * (1.0 + config.coastalBonus); double value = noiseField.Sample(tile.Center); if (flags[tile].IsLand) { if (!config.coastalLandHeight.IsEmpty) { num = Rmath.Max(num, Rmath.Remap(value, config.coastalLandHeight)); } } else { if (!config.coastalWaterDepth.IsEmpty) { num = Rmath.Max(num, Rmath.Remap(value, config.coastalWaterDepth)); } num = Rmath.Max(num, config.riverDeltaDepth - (double)tileData[tile]); } mutableTileData[tile] = Rmath.Min(mutableTileData[tile], num); } return mutableTileData; } private Config ConvertHeightBasedConfigToDistanceBased() { Config result = default(Config); result.coastalBonus = distanceBasedConfig.coastalBonus; result.startingArea = LandTarget(distanceBasedConfig.startingArea); result.fault = LandTarget(distanceBasedConfig.fault); result.river = new Rmath.RemapConfig { fromA = distanceBasedConfig.river.fromA, fromB = distanceBasedConfig.river.fromB, toA = LandTarget(distanceBasedConfig.river.toA), toB = LandTarget(distanceBasedConfig.river.toB) }; result.coastalNoiseScale = distanceBasedConfig.coastalNoiseScale; result.coastalLandHeight = new Rmath.RemapConfig { From = distanceBasedConfig.coastalLandHeight.From, toA = LandTarget(distanceBasedConfig.coastalLandHeight.toA), toB = LandTarget(distanceBasedConfig.coastalLandHeight.toB) }; result.coastalWaterDepth = new Rmath.RemapConfig { From = distanceBasedConfig.coastalWaterDepth.From, toA = WaterTarget(distanceBasedConfig.coastalWaterDepth.toA), toB = WaterTarget(distanceBasedConfig.coastalWaterDepth.toB) }; result.riverDeltaDepth = WaterTarget(distanceBasedConfig.riverDeltaDepth); return result; } private double LandTarget(double height) { return shapingConfig.land.Inverse(height); } private double WaterTarget(double height) { return shapingConfig.water.Inverse(height); } private TileData RiverMouthProximity() { TileSet tileSet = new TileSet(tiling); foreach (Tile tile in tiling.Tiles) { if (rivers[tile].IsRiverMouth()) { tileSet.Add(tiling.GetTile(rivers[tile].outletId)); } } return tiling.DistanceTransform((Func)tileSet.Contains, (Predicate)delegate(Tile tile) { ExtendedTileFlags extendedTileFlags = tile.Data(flags); return extendedTileFlags.IsOcean && extendedTileFlags.IsCoast; }); } } public class DifferentialInclineCalculator { [Serializable] public struct Config { public double mult; public double relativeNoiseScale; public double relativeNoisePower; public double absoluteNoiseScale; public double absoluteNoiseMagnitude; public RiverConfig rivers; public WindConfig wind; public FlatConfig flats; public PenaltyConfig penalties; } [Serializable] public struct RiverConfig { public enum Method { None, GeoMean, NormDiff, Hybrid } public Method method; public double scale; public double power; public double p2; public double mix; } [Serializable] public struct WindConfig { public double noiseScale; public StaticArray noiseCurve; public double radialStrength; } [Serializable] public struct FlatConfig { public double noiseScale; public double threshold; public double multiplier; } [Serializable] public struct PenaltyConfig { public double partition; public double barrierScale; public double barrierMargin; public double barrierPenalty; public double flatlandPenalty; public double riverConnection; public double riverAdjacency; } private readonly Config config; private readonly Predicate predefinedFlatland; private readonly TileData landmassPartitions; private readonly TileData riverGraph; private readonly TileData basins; private readonly NoiseField relativeNoise; private readonly NoiseField absoluteNoise; private readonly VectorNoiseField windNoise; private readonly PiecewiseCurve windNoiseCurve; private readonly NoiseField flatNoise; private readonly VoronoiCellIdNoise barrierNoise; private readonly RandomIndexer barrierRandom; public DifferentialInclineCalculator(RandomDirectory random, Config config, Predicate predefinedFlatland, TileData landmassPartitions, TileData riverGraph, TileData basins) { this.config = config; this.predefinedFlatland = predefinedFlatland; this.landmassPartitions = landmassPartitions; this.riverGraph = riverGraph; this.basins = basins; relativeNoise = random.BiasedNoise2D("InclineRelative", config.relativeNoiseScale); absoluteNoise = random.Noise2D("InclineAbsolute", config.absoluteNoiseScale); windNoise = ((config.wind.noiseScale > 0.0) ? random.WarpNoise("Wind", new VectorNoiseField.AbsoluteWarpConfig { period = config.wind.noiseScale, intensity = 1.0 / config.wind.noiseScale }) : VectorNoiseField.Flat()); windNoiseCurve = new PiecewiseCurve(config.wind.noiseCurve); flatNoise = random.BiasedNoise2D("Flat", config.flats.noiseScale); barrierNoise = random.CellNoise("BarrierCells", config.penalties.barrierScale); barrierRandom = random.Indexer("BarrierValues"); } public double Incline(Tile from, Tile to) { double mult = config.mult; Rfloat2 rfloat = 0.5 * (from.Center + to.Center); Rfloat2 normalized = (to.Center - from.Center).Normalized; mult *= RiverMultiplier(from, to); mult *= 1.0 + Rfloat2.Dot(GetWind(rfloat), normalized); if (flatNoise.Sample(rfloat) < config.flats.threshold || (predefinedFlatland(from) && predefinedFlatland(to))) { mult *= config.flats.multiplier; } mult *= Rmath.Pow(2.0 * Rmath.Clamp01(relativeNoise.Sample(rfloat)), config.relativeNoisePower); if (landmassPartitions[from] != landmassPartitions[to]) { mult += config.penalties.partition; } mult += config.absoluteNoiseMagnitude * absoluteNoise.Sample(rfloat); mult += BarrierPenalty(from, to); if (predefinedFlatland(from) != predefinedFlatland(to)) { mult += config.penalties.flatlandPenalty; } if (riverGraph[from].IsOutletOf(to) || riverGraph[to].IsOutletOf(from)) { mult += config.penalties.riverConnection; } else if (riverGraph[from].IsRiver() || riverGraph[to].IsRiver()) { mult += config.penalties.riverAdjacency; } return Rmath.Max(0.0, mult); } private double RiverMultiplier(Tile from, Tile to) { double num = RiverStrength(from); double num2 = RiverStrength(to); switch (config.rivers.method) { case RiverConfig.Method.None: return 1.0; case RiverConfig.Method.GeoMean: { double value3 = Rmath.Sqrt(num * num2) / (config.rivers.scale * config.rivers.scale); return 1.0 / (1.0 + Rmath.Pow(value3, config.rivers.power)); } case RiverConfig.Method.NormDiff: return Rmath.Pow(Rmath.Abs(num - num2) / (num + num2), config.rivers.p2); case RiverConfig.Method.Hybrid: { double value = Rmath.Sqrt(num * num2) / (config.rivers.scale * config.rivers.scale); value = Rmath.Pow(value, config.rivers.power); double value2 = Rmath.Abs(num - num2) / (num + num2); value2 = Rmath.Pow(value2, config.rivers.p2); return Rmath.Lerp(0.5, 1.0 / (1.0 + value), value2); } default: return 0.0; } } private double RiverStrength(Tile tile) { return basins[tile].catchment; } private Rfloat2 GetWind(Rfloat2 midpoint) { Rfloat2 rfloat = config.wind.radialStrength * midpoint.Normalized; Rfloat2 rfloat2 = windNoise.Sample(midpoint); double length = rfloat2.Length; if (length < 1E-06) { return rfloat; } return windNoiseCurve.Eval(length) / length * rfloat2 + rfloat; } private double BarrierPenalty(Tile from, Tile to) { int num = barrierNoise.Sample(from.Center); int num2 = barrierNoise.Sample(to.Center); double num3 = Rmath.Abs(barrierRandom.Single(num).Value - barrierRandom.Single(num2).Value); if (num != num2 && num3 < config.penalties.barrierMargin) { return config.penalties.barrierPenalty; } return 0.0; } } [Producer(new string[] { "Heights/Flatlands" })] [Fork("Region")] public class FlatlandsStage : IGeneratorStage> { [Serializable] public struct Config { public bool useBasins; public double noiseScale; public NoiseField.FractalConfig noiseFractal; public VectorNoiseField.RelativeWarpConfig warp; public double noiseThreshold; } public const string OUTPUT = "Heights/Flatlands"; [Random("Heights/Flatlands")] public RandomDirectory random; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Base")] public TileData flags; [Resource("PolarProximity")] public PolarProximityMap polarProximity; [Resource("Landmasses/Artifacts")] public TileData artifacts; public TileData Compute() { MutableTileData mutableTileData = tiling.CreateData(init: false); NoiseField noiseField = random.BiasedNoise2D("Noise", config.noiseScale).Fractal(config.noiseFractal); VectorNoiseField vectorNoiseField = random.WarpNoise("Warp", config.noiseScale, config.warp); foreach (Tile tile in tiling.Tiles) { if (artifacts[tile].isBasin && config.useBasins) { mutableTileData[tile] = true; } if (noiseField.Sample(tile.Center + vectorNoiseField.Sample(tile.Center)) < config.noiseThreshold) { mutableTileData[tile] = true; } } return mutableTileData; } } [Serializable] [CompositeConfig] public record HeightConfig { public RoughnessAnalyzer.Config roughnessAnalysis; public RegionalConfig flatlands; public RegionalConfig lakes; public RegionalConfig boundary; public RegionalConfig incline; public RegionalConfig shaping; public RegionalConfig mountainEdgeProximity; public RegionalConfig mountainQuality; public RegionalConfig settlement; } [Producer(new string[] { "Heights/JoinedHeights" })] public class HeightJoinStage : IGeneratorStage> { public const string OUTPUT = "Heights/JoinedHeights"; [Resource("MainTiling")] public Tiling tiling; [Resource("Regions/Tiling")] public Tiling[] regionTilings; [Resource("Regions/Mapping")] public SubtilingMapping[] regionMappings; [Resource("Biomes/PostprocessedHeightMap")] public TileData[] heights; public TileData Compute() { return RegionStage.JoinRegionData(tiling, regionTilings, regionMappings, heights); } } [Producer(new string[] { "Heights/SettledHeightMap" })] [Fork("Region")] public class HeightSettlementStage : IGeneratorStage> { [Serializable] public struct Config { public double hydraulicErosionRate; public double mountainCutdownRatio; public double mountainMinPatchSize; public DropletErosionSimulator.Config hydraulicErosion; public ImpactErosionSimulator.Config impactErosion; public double erosionTemperature; public double settlementTemperature; public double polarTrenchWidth; } private class ErosionGuide { private readonly TileHeap targets; private readonly RandomMultiSequence random; private readonly TileData mountainQuality; private readonly double temperature; public bool IsEmpty => targets.Count == 0; public Tile Next => targets.Peek(); public ErosionGuide(HeapType heapType, RandomMultiSequence random, Tiling tiling, TileData mountainQuality, double temperature) { targets = new TileHeap(tiling, heapType); this.random = random; this.mountainQuality = mountainQuality; this.temperature = temperature; } public int Update(Tile tile, bool viable) { bool flag = targets.Contains(tile); if (!viable) { targets.Remove(tile); if (!flag) { return 0; } return -1; } targets.AddOrUpdate(tile, Weight(tile)); return (!flag) ? 1 : 0; } private double Weight(Tile tile) { return random.Single(tile.Id).Gumbel(Rmath.Log(mountainQuality[tile]), temperature); } } public const string OUTPUT = "Heights/SettledHeightMap"; [Random("Heights/SettledHeightMap")] public RandomDirectory random; [Config] public CommonConfig commonConfig; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Heights/ShapedHeights")] public TileData input; [Resource("PolarProximity")] public PolarProximityMap polarProximity; [Resource("Flags/Extended")] public TileData flags; [Resource("PrunedRivers")] public TileData prunedRivers; [Resource("Heights/MountainQuality")] public TileData mountainQuality; private const double MOUNTAIN_REMOVAL_CUTDOWN = 4.0; public TileData Compute() { MutableTileData mutableTileData = input.Copy(); DoHydraulicErosion(mutableTileData, config.hydraulicErosionRate); SettleHeights(mutableTileData, 1.0 - config.mountainCutdownRatio); CullMountains(mutableTileData, config.mountainMinPatchSize); EnsurePolarTrenches(mutableTileData); SinkWorldEdge(mutableTileData); return mutableTileData; } private void SettleHeights(MutableTileData heights, double targetRatio) { if (config.impactErosion.rate <= 0.0) { return; } ImpactErosionSimulator impactErosionSimulator = new ImpactErosionSimulator(config.impactErosion, (Tile _) => true); ErosionGuide erosionGuide = new ErosionGuide(HeapType.Min, random.MultiSequence("ImpactErosion"), tiling, mountainQuality, config.settlementTemperature); int num = 0; foreach (Tile tile in tiling.Tiles) { if (!((double)heights[tile] < commonConfig.mountainHeight)) { num += erosionGuide.Update(tile, viable: true); } } int num2 = Rmath.CeilToInt((double)tiling.Count * targetRatio); while (!erosionGuide.IsEmpty && num > num2) { foreach (Tile item in impactErosionSimulator.RunAt(heights, erosionGuide.Next)) { num += erosionGuide.Update(item, (double)heights[item] >= commonConfig.mountainHeight); } } } private void DoHydraulicErosion(MutableTileData heights, double rate) { if (rate <= 0.0) { return; } DropletErosionSimulator dropletErosionSimulator = new DropletErosionSimulator(config.hydraulicErosion, (Tile _) => true, (Tile tile) => flags[tile].IsWater || prunedRivers[tile].IsRiver()); ErosionGuide erosionGuide = new ErosionGuide(HeapType.Max, random.MultiSequence("HydraulicErosion"), tiling, mountainQuality, config.erosionTemperature); int num = Rmath.FloorToInt((double)tiling.Count * rate); foreach (Tile tile in tiling.Tiles) { if (!((double)heights[tile] < commonConfig.mountainHeight)) { erosionGuide.Update(tile, viable: true); } } for (int i = 0; i < num; i++) { foreach (Tile item in dropletErosionSimulator.RunAt(heights, erosionGuide.Next)) { erosionGuide.Update(item, viable: true); } } } private void EnsurePolarTrenches(MutableTileData heights) { foreach (Tile tile in tiling.Tiles) { if (flags[tile].IsOcean) { double t = Rmath.InverseLerp(Rmath.Abs(polarProximity(tile.Center)), config.polarTrenchWidth, 0.0); heights[tile] = Rmath.SmoothStep(t, heights[tile], commonConfig.oceanDepth); } } } private void SinkWorldEdge(MutableTileData heights) { foreach (Tile tile in tiling.Tiles) { if (tile.Center.Length > commonConfig.worldSize - commonConfig.tileSpacing) { tile.Data(heights) = commonConfig.oceanDepth; } } } private void CullMountains(MutableTileData heights, double minPatchSize) { foreach (Tile item in FindSmallMountains(heights, minPatchSize).ConsistentOrdering()) { item.Data(heights) = commonConfig.mountainHeight - 4.0; } } private TileSet FindSmallMountains(TileData heights, double minPatchSize) { TileSet tileSet = new TileSet(tiling); TileSet tileSet2 = new TileSet(tiling); TileQueue tileQueue = new TileQueue(tiling); double num = minPatchSize * minPatchSize; foreach (Tile tile2 in tiling.Tiles) { if ((double)tile2.Data(heights) < commonConfig.mountainHeight || tileSet2.Contains(tile2)) { continue; } TileSet tileSet3 = new TileSet(tiling); double num2 = 0.0; tileSet2.Add(tile2); tileQueue.Enqueue(tile2); while (tileQueue.Count > 0) { Tile tile = tileQueue.Dequeue(); tileSet3.Add(tile); num2 += tile.Area; foreach (Tile neighbor in tile.GetNeighbors()) { if (!tileSet2.Contains(neighbor) && (double)neighbor.Data(heights) >= commonConfig.mountainHeight) { tileSet2.Add(neighbor); tileQueue.Enqueue(neighbor); } } } if (!(num2 >= num)) { tileSet.UnionWith(tileSet3); } } return tileSet; } } [Producer(new string[] { "Heights/ShapedHeights" })] [Fork("Region")] public class HeightShapingStage : IGeneratorStage> { [Serializable] public struct Config { public ProfileConfig land; public ProfileConfig water; } [Serializable] public struct ProfileConfig { public enum ProfileType { Linear, SmoothRamp, Saturated } public ProfileType type; public ProfileUtility.SmoothRamp.Config smoothRamp; public ProfileUtility.Saturated.Config saturated; public double bias; public double Evaluate(double input) { double num = bias; return num + type switch { ProfileType.Linear => input, ProfileType.SmoothRamp => ProfileUtility.SmoothRamp.Profile(input, in smoothRamp), ProfileType.Saturated => ProfileUtility.Saturated.Profile(input, in saturated), _ => throw new ArgumentOutOfRangeException(), }; } public double Inverse(double input) { return type switch { ProfileType.Linear => input - bias, ProfileType.SmoothRamp => ProfileUtility.SmoothRamp.Inverse(input - bias, in smoothRamp), ProfileType.Saturated => ProfileUtility.Saturated.Inverse(input - bias, in saturated), _ => throw new ArgumentOutOfRangeException(), }; } } public const string OUTPUT = "Heights/ShapedHeights"; [Config] public CommonConfig commonConfig; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Heights/DifferentialHeightMap")] public TileData input; [Resource("Flags/Extended")] public TileData flags; public TileData Compute() { return tiling.CreateData((Func)((Tile tile) => flags[tile].IsWater ? Rmath.Clamp(config.water.Evaluate(input[tile]), commonConfig.oceanDepth, 0.0) : config.land.Evaluate(input[tile]))); } } public static class HeightUtility { public struct Roughness { public InertFloat variance; public InertFloat laplacian; public InertFloat2 gradient; public Rfloat2 Gradient => gradient; public double Steepness => ((Rfloat2)gradient).Length; } public static Rfloat2 InclineVector(this TileData heights, Tile t1, Tile t2) { return InclineVector(t1.Center, t1.Data(heights), t2.Center, t2.Data(heights)); } public static Rfloat2 InclineVector(Rfloat2 v1, double h1, Rfloat2 v2, double h2) { Rfloat2 rfloat = v2 - v1; return (h2 - h1) * rfloat / rfloat.SqrLength; } public static Roughness ComputeRoughness(Tile tile, TileData heights) { double num = 0.0; Rfloat2 zero = Rfloat2.Zero; double num2 = 0.0; double num3 = 0.0; foreach (Tile neighbor in tile.GetNeighbors()) { Rfloat2 rfloat = neighbor.Center - tile.Center; double length = rfloat.Length; double num4 = (double)heights[neighbor] - (double)heights[tile]; double num5 = tile.Tiling.Spacing / length; num += num5 * num4 * num4 / (length * length); zero += num5 * num4 / (length * length) * rfloat; num2 += num5 * num4 / length; num3 += num5; } Roughness result = default(Roughness); if (num3 == 0.0) { return result; } result.variance = Rmath.Sqrt(num / num3); result.gradient = zero; result.laplacian = num2 / num3; return result; } } [Producer(new string[] { "Heights/Lakes" })] [Fork("Region")] public class LakeStage : IGeneratorStage { [Serializable] public struct Config { public double budget; public AffinityConfig affinity; public double minCoastalProximity; public double expansionNoiseScale; public double expansionVariability; public double expansion; } [Serializable] public struct AffinityConfig { public double noiseScale; public double kNoise; public double kFlatland; public double kCuriosity; } public const string OUTPUT = "Heights/Lakes"; [Random("Heights/Lakes")] public RandomDirectory random; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Base")] public TileData flags; [Resource("CoastalProximity")] public TileData coastalProximity; [Resource("Rivers/Curiosities")] public TileData riverCuriosities; [Resource("Heights/Flatlands")] public TileData flatlands; public TileSet Compute() { NoiseField noiseField = random.BiasedNoise2D("Placement", config.affinity.noiseScale); TileHeap tileHeap = new TileHeap(tiling, HeapType.Max); foreach (Tile tile in tiling.Tiles) { if (CanBeLake(tile)) { double num = 0.0; num += config.affinity.kNoise * noiseField.Sample(tile.Center); num += config.affinity.kFlatland * (double)(flatlands[tile] ? 1 : 0); num += config.affinity.kCuriosity * (double)riverCuriosities[tile]; tileHeap.AddOrUpdate(tile, num); } } int num2 = Rmath.RoundToInt(config.budget * (double)tiling.Count); TileSet tileSet = new TileSet(tiling); while (tileHeap.Count > 0 && num2-- > 0) { tileSet.Add(tileHeap.Pop()); } return Expand(tileSet); } private TileSet Expand(TileSet tiles) { TileData tileData = tiling.DistanceTransform((Tile tile) => (!tiles.Contains(tile)) ? null : new double?(0.0), CanBeLake); NoiseField noiseField = random.Noise2D("Expansion", config.expansionNoiseScale); TileSet tileSet = new TileSet(tiling); foreach (Tile tile in tiling.Tiles) { if ((double)tileData[tile] <= config.expansion * (1.0 + config.expansionVariability * noiseField.Sample(tile.Center))) { tileSet.Add(tile); } } return tileSet; } private bool CanBeLake(Tile tile) { if (flags[tile].IsLand) { return (double)coastalProximity[tile] > config.minCoastalProximity; } return false; } } public class RoughnessAnalyzer { [Serializable] public struct Config { public LinearConfig flat; public LinearConfig saddle; public LinearConfig spine; public LinearConfig valley; } [Serializable] public record LinearConfig { public double c; public double pVar; public double pGrad; public double pLapl; public double pAbsLapl; } private readonly Config config; public RoughnessAnalyzer(Config config) { this.config = config; } public double FlatScore(HeightUtility.Roughness roughness) { return LinearFit(roughness, config.flat); } public double SaddleScore(HeightUtility.Roughness roughness) { return LinearFit(roughness, config.saddle); } public double SpineScore(HeightUtility.Roughness roughness) { return LinearFit(roughness, config.spine); } public double ValleyScore(HeightUtility.Roughness roughness) { return LinearFit(roughness, config.valley); } private static double LinearFit(HeightUtility.Roughness roughness, LinearConfig cfg) { if (cfg == null) { return 0.0; } return Functions.Sigmoid(cfg.c + cfg.pVar * (double)roughness.variance + cfg.pGrad * roughness.Steepness + cfg.pLapl * (double)roughness.laplacian + cfg.pAbsLapl * Rmath.Abs(roughness.laplacian)); } } [Producer(new string[] { "Heights/RoughnessAnalyzer" })] public class RoughnessAnalyzerStage : IGeneratorStage { public const string OUTPUT = "Heights/RoughnessAnalyzer"; [Config] public RoughnessAnalyzer.Config config; public RoughnessAnalyzer Compute() { return new RoughnessAnalyzer(config); } } [Producer(new string[] { "Heights/Roughness" })] [Fork("Region")] public class RoughnessStage : IGeneratorStage> { public const string OUTPUT = "Heights/Roughness"; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Heights/SettledHeightMap")] public TileData heights; public TileData Compute() { return tiling.CreateData((Tile tile) => HeightUtility.ComputeRoughness(tile, heights)); } } } namespace Riverheim.World.Height.Mountains { [Producer(new string[] { "Heights/MountainEdgeProximity" })] [Fork("Region")] public class MountainEdgeProximityStage : IGeneratorStage> { [Serializable] public struct Config { public double trim; public double trimNoiseScale; public double trimNoiseMagnitude; } public const string OUTPUT = "Heights/MountainEdgeProximity"; [Random("Heights/MountainEdgeProximity")] public RandomDirectory random; [Config] public CommonConfig commonConfig; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Heights/ShapedHeights")] public TileData input; public TileData Compute() { NoiseField noise = random.BiasedNoise2D("MountainTrim", config.trimNoiseScale); return tiling.DistanceTransform((Tile tile) => tile.GetNeighbors().Any((Tile neighbor) => !IsMountain(neighbor)) ? new double?(0.0 - config.trim + config.trimNoiseMagnitude * noise.Sample(tile.Center)) : null, IsMountain); } private bool IsMountain(Tile tile) { return (double)input[tile] >= commonConfig.mountainHeight; } } [Producer(new string[] { "Heights/MountainPartitions", "Heights/MountainPartitionsOverstretch" })] [Fork("Region")] public class MountainPartitioningStage : IGeneratorStage<(TileData, TileData)> { public const string PARTITIONS = "Heights/MountainPartitions"; public const string OVERSTRETCH = "Heights/MountainPartitionsOverstretch"; public const int LOWLAND = -1; public const int FULLY_TRIMMED = -2; [Config] public CommonConfig commonConfig; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Heights/ShapedHeights")] public TileData heights; [Resource("Heights/MountainEdgeProximity")] public TileData proximity; public (TileData, TileData) Compute() { MutableTileData mutableTileData = CreatePartitions(); MutableTileData mutableTileData2 = tiling.CreateData(); ExpandPartitions(mutableTileData, mutableTileData2); return (mutableTileData, mutableTileData2); } private MutableTileData CreatePartitions() { return tiling.MarkConnectedComponents((Tile tile) => IsMountain(tile) && (double)proximity[tile] >= 0.0); } private void ExpandPartitions(MutableTileData mountainIds, MutableTileData overstretch) { TileSet tileSet = new TileSet(tiling); foreach (Tile tile3 in tiling.Tiles) { if (IsMountain(tile3) && mountainIds[tile3] == -1) { tileSet.Add(tile3); } } TileQueue tileQueue = new TileQueue(tiling); foreach (Tile item in tileSet.ConsistentOrdering()) { if (item.GetNeighbors().Any((Tile neighbor) => mountainIds[neighbor] != -1)) { tileSet.Remove(item); tileQueue.Enqueue(item); } } while (tileQueue.Count > 0) { Tile tile = tileQueue.Dequeue(); Tile tile2 = default(Tile); int num = -2; double num2 = double.MaxValue; foreach (Tile neighbor in tile.GetNeighbors()) { if (!IsMountain(neighbor)) { continue; } if (mountainIds[neighbor] != -1) { double sqrLength = (tile.Center - neighbor.Center).SqrLength; if (sqrLength < num2) { tile2 = neighbor; num2 = sqrLength; num = mountainIds[neighbor]; } } else { tileSet.Remove(neighbor); tileQueue.Enqueue(neighbor); } } mountainIds[tile] = num; overstretch[tile] = (double)overstretch[tile2] + Rmath.Sqrt(num2); } foreach (Tile item2 in tileSet.Unordered()) { mountainIds[item2] = -2; } } private bool IsMountain(Tile tile) { return (double)heights[tile] >= commonConfig.mountainHeight; } } [Producer(new string[] { "Heights/MountainQuality" })] [Fork("Region")] public class MountainQualityStage : IGeneratorStage> { [Serializable] public struct PartitionScoringConfig { public double trimmed; public double area; public double span; public double neighbors; } [Serializable] public struct Config { public PartitionScoringConfig partitionScoring; public double heightScoringMultiplier; public MountainUtility.HeightScoreConfig heightScoring; public double localScoreMultiplier; public MountainUtility.RoughnessScoreConfig roughnessScoring; public double distanceScoreMultiplier; public double temperature; public bool useNewScore; public MountainUtility.MountainScoringConfig localScoring; } private record PartitionTraits { public double area; public double span; public HashSet neighbors; } public const string OUTPUT = "Heights/MountainQuality"; [Config] public CommonConfig commonConfig; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Heights/ShapedHeights")] public TileData heights; [Resource("Heights/MountainEdgeProximity")] public TileData proximity; [Resource("Heights/MountainPartitions")] public TileData partitions; [Resource("Heights/RoughnessAnalyzer")] public RoughnessAnalyzer roughnessAnalyzer; public TileData Compute() { Dictionary traits = ComputeTraits(); return tiling.CreateData((Tile tile) => (partitions[tile] == -1) ? ((InertFloat)0.0) : ((InertFloat)Score(tile, partitions[tile], traits[partitions[tile]]))); } private double Score(Tile tile, int partitionId, PartitionTraits traits) { double num = 0.0; if (partitionId == -2) { num += config.partitionScoring.trimmed; } else { num += config.partitionScoring.area * Rmath.Sqrt(traits.area); num += config.partitionScoring.span * traits.span; num += config.partitionScoring.neighbors * (double)traits.neighbors.Count; } if (config.useNewScore) { num += config.localScoreMultiplier * MountainUtility.MountainScore(heights[tile], commonConfig.mountainHeight, HeightUtility.ComputeRoughness(tile, heights), roughnessAnalyzer, config.localScoring); } else { num += config.heightScoringMultiplier * ((double)heights[tile] - commonConfig.mountainHeight); num += config.localScoreMultiplier * MountainUtility.MountainRoughnessScore(HeightUtility.ComputeRoughness(tile, heights), config.roughnessScoring); } num += config.distanceScoreMultiplier * tile.Center.Length; return Functions.Sigmoid(num, config.temperature); } private Dictionary ComputeTraits() { Dictionary dictionary = new Dictionary(); foreach (Tile tile in tiling.Tiles) { int num = partitions[tile]; if (num == -1) { continue; } if (!dictionary.ContainsKey(num)) { dictionary[num] = new PartitionTraits { area = 0.0, span = double.MinValue, neighbors = new HashSet() }; } PartitionTraits partitionTraits = dictionary[num]; partitionTraits.area += tile.Area; partitionTraits.span = Rmath.Max(partitionTraits.span, proximity[tile]); foreach (Tile neighbor in tile.GetNeighbors()) { if (partitions[neighbor] != num && partitions[neighbor] != -1) { partitionTraits.neighbors.Add(partitions[neighbor]); } } } return dictionary; } } public static class MountainUtility { [Serializable] public struct HeightScoreConfig { public double headroom; public double power; } [Serializable] public struct RoughnessScoreConfig { public double flatnessScale; public double peakinessMidpoint; public double peakinessStrength; public double kFlatness; public double kPeakiness; public double kSaddle; } [Serializable] public struct MountainScoringConfig { public double headroom; public double k0; public double kFlat; public double kSpine; public double kValley; public double kSaddle; public double kHeight; public double kOvershoot; } public static double MountainHeightScore(double height, CommonConfig commonConfig, HeightScoreConfig config) { double num = commonConfig.mountainHeight + config.headroom; return 1.0 - Functions.Saturation(num - height, config.headroom, config.power); } public static double MountainRoughnessScore(HeightUtility.Roughness roughness, RoughnessScoreConfig config) { double num = Rmath.Exp((0.0 - config.flatnessScale) * (double)roughness.variance); double num2 = (double)(-Rmath.Sign(roughness.laplacian)) * Functions.Saturation(Rmath.Abs(roughness.laplacian), config.peakinessMidpoint, config.peakinessStrength); double num3 = (1.0 - num) * (1.0 - Rmath.Abs(num2)); return config.kFlatness * num + config.kPeakiness * num2 + config.kSaddle * num3; } public static double MountainScore(double height, double thresholdHeight, HeightUtility.Roughness roughness, RoughnessAnalyzer analyzer, MountainScoringConfig config) { return config.k0 + config.kFlat * analyzer.FlatScore(roughness) + config.kSpine * analyzer.SpineScore(roughness) + config.kValley * analyzer.ValleyScore(roughness) + config.kSaddle * analyzer.SaddleScore(roughness) + config.kHeight * (height - thresholdHeight) + config.kOvershoot * Rmath.Max(0.0, height - thresholdHeight - config.headroom); } public static double NormalizedMountainScore(double height, double thresholdHeight, HeightUtility.Roughness roughness, RoughnessAnalyzer analyzer, MountainScoringConfig config, double temp) { return Functions.Sigmoid(MountainScore(height, thresholdHeight, roughness, analyzer, config), temp); } } } namespace Riverheim.World.Biomes { public enum Biome : short { None, Ocean, Meadows, Marsh, Forest, Swamp, Mountain, Plains, Mistlands, Mistglade, Ashlands, Lavaplain, DeepNorth } [Serializable] public struct AllBiomeConfig { [CompilerGenerated] private sealed class d__13 : IEnumerable<(Biome, T)>, IEnumerable, IEnumerator<(Biome, T)>, IEnumerator, IDisposable { private int <>1__state; private (Biome, T) <>2__current; private int <>l__initialThreadId; public AllBiomeConfig <>4__this; public AllBiomeConfig <>3__<>4__this; (Biome, T) IEnumerator<(Biome, T)>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__13(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (Biome.Ocean, <>4__this.ocean); <>1__state = 1; return true; case 1: <>1__state = -1; <>2__current = (Biome.Meadows, <>4__this.meadows); <>1__state = 2; return true; case 2: <>1__state = -1; <>2__current = (Biome.Forest, <>4__this.forest); <>1__state = 3; return true; case 3: <>1__state = -1; <>2__current = (Biome.Marsh, <>4__this.marsh); <>1__state = 4; return true; case 4: <>1__state = -1; <>2__current = (Biome.Swamp, <>4__this.swamp); <>1__state = 5; return true; case 5: <>1__state = -1; <>2__current = (Biome.Plains, <>4__this.plains); <>1__state = 6; return true; case 6: <>1__state = -1; <>2__current = (Biome.Mountain, <>4__this.mountain); <>1__state = 7; return true; case 7: <>1__state = -1; <>2__current = (Biome.Mistlands, <>4__this.mistlands); <>1__state = 8; return true; case 8: <>1__state = -1; <>2__current = (Biome.Mistglade, <>4__this.mistglade); <>1__state = 9; return true; case 9: <>1__state = -1; <>2__current = (Biome.Ashlands, <>4__this.ashlands); <>1__state = 10; return true; case 10: <>1__state = -1; <>2__current = (Biome.Lavaplain, <>4__this.lavaplain); <>1__state = 11; return true; case 11: <>1__state = -1; <>2__current = (Biome.DeepNorth, <>4__this.deepNorth); <>1__state = 12; return true; case 12: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<(Biome, T)> IEnumerable<(Biome, T)>.GetEnumerator() { d__13 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__13(0); } d__.<>4__this = <>3__<>4__this; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<(Biome, T)>)this).GetEnumerator(); } } public T ocean; public T meadows; public T marsh; public T forest; public T swamp; public T mountain; public T plains; public T mistlands; public T mistglade; public T ashlands; public T lavaplain; public T deepNorth; public readonly T Get(Biome biome) { return biome switch { Biome.Ocean => ocean, Biome.Meadows => meadows, Biome.Marsh => marsh, Biome.Forest => forest, Biome.Swamp => swamp, Biome.Mountain => mountain, Biome.Plains => plains, Biome.Mistlands => mistlands, Biome.Mistglade => mistglade, Biome.Ashlands => ashlands, Biome.Lavaplain => lavaplain, Biome.DeepNorth => deepNorth, _ => default(T), }; } [IteratorStateMachine(typeof(AllBiomeConfig<>.d__13))] public IEnumerable<(Biome, T)> All() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__13(-2) { <>3__<>4__this = this }; } } [Producer(new string[] { "Biomes/Affinities" })] [Fork("Region")] public class BiomeAffinityStage : IGeneratorStage> { public const string OUTPUT = "Biomes/Affinities"; [Config] public CommonConfig commonConfig; [LaneConfig] public AllBiomeConfig config; [Random("Biomes/Affinities")] public RandomDirectory random; [Resource("Flags/Base")] public TileData flags; [Resource("Heights/SettledHeightMap")] public TileData heights; [Resource("TravelDistance")] public TileData travelDistance; [Resource("CoastalProximity")] public TileData coastalProximity; [Resource("Rivers/Proximity")] public TileData riverProximity; [Resource("PrunedRivers")] public TileData rivers; [Resource("Heights/Roughness")] public TileData roughness; public Dictionary Compute() { Dictionary dictionary = new Dictionary(); foreach (var item3 in config.All()) { Biome item = item3.Item1; BiomeAffinity.Config item2 = item3.Item2; dictionary[item] = new BiomeAffinity(random.Sub(item.ToString()), commonConfig, item2, flags, heights, travelDistance, coastalProximity, riverProximity, rivers, roughness); } return dictionary; } } [Serializable] [CompositeConfig] public record BiomeConfig { public RegionalConfig> affinity; public RegionalConfig conditionalPlacement; public RegionalConfig competitiveScoring; public RegionalConfig competitivePlacement; public RegionalConfig replacementScoring; public RegionalConfig replacement; public RegionalConfig postprocessing; } [Serializable] public struct BiomePostprocessingConfig { public double meadowsShapingOffset; public double meadowsShapingIntensity; public double meadowsForestShapingIntensity; public double meadowsForestThreshold; public double marshUnderwaterLerpingRangeMin; public double marshUnderwaterLerpingRangeMax; public double marshBaseHeight; public double swampUnderwaterLerpingRangeMin; public double swampUnderwaterLerpingRangeMax; public double swampBaseHeight; public double plainsShapingIntensity; public double plainsShapingOffset; public double plainSmoothingHeightThreshold; public double plainSmoothingRange; public double ashlandsHeightCeiling; public double ashlandsHeightSmoothing; } [Producer(new string[] { "Biomes/PostprocessedHeightMap" })] [Fork("Region")] public class BiomeHeightPostprocessor : IGeneratorStage> { public const string OUTPUT = "Biomes/PostprocessedHeightMap"; [Config] public CommonConfig commonConfig; [LaneConfig] public BiomePostprocessingConfig config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Heights/SettledHeightMap")] public TileData baseHeights; [Resource("Biomes")] public TileData biomes; [Resource("Forestation")] public TileData forestation; public TileData Compute() { MutableTileData mutableTileData = tiling.CreateData((Func)((Tile tile) => GetAdjustedHeight(tile.Data(biomes), tile, tile.Data(baseHeights)))); SmoothPlains(mutableTileData); return mutableTileData; } private double GetAdjustedHeight(Biome biome, Tile tile, double height) { switch (biome) { case Biome.Meadows: { double meadowsShapingOffset = config.meadowsShapingOffset; double intensity = (((double)forestation[tile] > config.meadowsForestThreshold) ? config.meadowsForestShapingIntensity : config.meadowsShapingIntensity); return ParabolicShaping(height - meadowsShapingOffset, intensity) + meadowsShapingOffset; } case Biome.Plains: { double plainsShapingOffset = config.plainsShapingOffset; return ParabolicShaping(height - plainsShapingOffset, config.plainsShapingIntensity) + plainsShapingOffset; } case Biome.Marsh: return Rmath.Lerp(Rmath.InverseLerp(height, config.marshUnderwaterLerpingRangeMin, config.marshUnderwaterLerpingRangeMax), height, config.marshBaseHeight); case Biome.Swamp: return Rmath.Lerp(Rmath.InverseLerp(height, config.swampUnderwaterLerpingRangeMin, config.swampUnderwaterLerpingRangeMax), height, config.swampBaseHeight); case Biome.Ashlands: return Smoothing.Min(height, config.ashlandsHeightCeiling, config.ashlandsHeightSmoothing); case Biome.Lavaplain: return height; default: return height; } } private double ParabolicShaping(double height, double intensity) { double num = Rmath.Clamp01(height / commonConfig.mountainHeight); if (height > 0.0) { height -= intensity * height * (1.0 - num); } return height; } private bool IsSmoothablePlainsTile(Tile tile, TileData heights) { if (tile.Data(biomes) != Biome.Plains) { return false; } if ((double)tile.Data(heights) <= config.plainSmoothingHeightThreshold) { return false; } return true; } private void SmoothPlains(MutableTileData heights) { MutableTileData originalHeights = heights.Copy(); foreach (Tile tile in tiling.Tiles) { if (IsSmoothablePlainsTile(tile, originalHeights)) { tile.Data(heights) = tile.Smooth(originalHeights, config.plainSmoothingRange, 3.0, (Tile t) => IsSmoothablePlainsTile(t, originalHeights)); } } } } [Serializable] public struct BiomeReplacementConfig { public Biome source; public double rate; public SingleBiomePlacement.Config placement; public bool IsTrivial { get { if (source != 0) { return rate <= 0.0; } return true; } } } [Producer(new string[] { "Biomes" })] [Fork("Region")] public class BiomeReplacementStage : IGeneratorStage> { [Serializable] public struct Config { public AllBiomeConfig biomes; public Dictionary> ReplacementMap() { Dictionary> dictionary = new Dictionary>(); foreach (var (item, biomeReplacementConfig) in biomes.All()) { if (!biomeReplacementConfig.IsTrivial) { Biome source = biomeReplacementConfig.source; if (!dictionary.ContainsKey(source)) { dictionary[source] = new List(); } dictionary[source].Add(item); } } return dictionary; } } public const string OUTPUT = "Biomes"; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Extended")] public TileData flags; [Resource("Biomes/Replacement/Scores")] public Dictionary> scores; [Resource("Biomes/Replacement/Origins")] public Dictionary origins; [Resource("Biomes/CompetitivePlacement")] public TileData biomes; public TileData Compute() { MutableTileData mutableTileData = biomes.Copy(); Dictionary> dictionary = config.ReplacementMap(); Dictionary dictionary2 = new Dictionary(); foreach (Biome key2 in dictionary.Keys) { dictionary2[key2] = new TileSet(tiling); } foreach (Tile tile in tiling.Tiles) { Biome key = biomes[tile]; if (dictionary.TryGetValue(key, out var _) && flags[tile].IsLand) { dictionary2[key].Add(tile); } } foreach (Biome key3 in dictionary.Keys) { ReplaceBiome(mutableTileData, dictionary2[key3], dictionary[key3]); } return mutableTileData; } private void ReplaceBiome(MutableTileData result, TileSet sourceTiles, List targetBiomes) { if (targetBiomes.Count > 1) { throw new NotImplementedException("Multiple replacement targets not supported yet."); } Biome biome = targetBiomes[0]; BiomeReplacementConfig biomeReplacementConfig = config.biomes.Get(biome); SingleBiomePlacement singleBiomePlacement = new SingleBiomePlacement(biome, biomeReplacementConfig.placement, scores[biome], origins[biome], sourceTiles); int num = Rmath.FloorToInt(biomeReplacementConfig.rate * (double)sourceTiles.Count); Tile tile; while (sourceTiles.Count > 0 && singleBiomePlacement.Next(out tile) && num-- > 0) { result[tile] = biome; } } } [Serializable] public struct BiomeReplacementAffinityConfig { public StaticArray boundaryProximity; public StaticArray patchRadius; } [Producer(new string[] { "Biomes/Replacement/Scores", "Biomes/Replacement/Origins" })] [Fork("Region")] public class BiomeReplacementScoreStage : IGeneratorStage<(Dictionary>, Dictionary)> { [Serializable] public struct Config { public AllBiomeConfig affinity; public BiomeAffinityNormalization.Config normalization; public double originSpacing; public double randomness; } private class ReplacementAffinity { public class Input { public TileData boundaryProximity; public TileData patchIds; public Dictionary patchRadii; } private readonly BiomeAffinity baseAffinity; private readonly PiecewiseCurve boundaryProximityCurve; private readonly PiecewiseCurve patchRadiusCurve; private readonly Input input; public ReplacementAffinity(Input input, BiomeAffinity baseAffinity, BiomeReplacementAffinityConfig affinityConfig) { this.input = input; this.baseAffinity = baseAffinity; boundaryProximityCurve = new PiecewiseCurve(affinityConfig.boundaryProximity); patchRadiusCurve = new PiecewiseCurve(affinityConfig.patchRadius); } public double Get(Tile tile) { return baseAffinity.Get(tile) + boundaryProximityCurve.Eval(input.boundaryProximity[tile]) + patchRadiusCurve.Eval(input.patchRadii[input.patchIds[tile]]); } } public const string SCORES = "Biomes/Replacement/Scores"; public const string ORIGINS = "Biomes/Replacement/Origins"; [Random("Biomes/ReplacementScoring")] public RandomDirectory random; [LaneConfig] public Config config; [LaneConfig] public BiomeReplacementStage.Config replacementConfig; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Extended")] public TileData flags; [Resource("Biomes/CompetitivePlacement")] public TileData biomes; [Resource("Biomes/Affinities")] public Dictionary baseAffinities; public (Dictionary>, Dictionary) Compute() { Dictionary> replacementMap = replacementConfig.ReplacementMap(); Dictionary affinities = ComputeAffinities(replacementMap); Dictionary> dictionary = ComputeScores(replacementMap, affinities); Dictionary item = ComputeOrigins(dictionary); return (dictionary, item); } private Dictionary ComputeAffinities(Dictionary> replacementMap) { Dictionary dictionary = new Dictionary(); foreach (Biome key in replacementMap.Keys) { ReplacementAffinity.Input input = ComputeInput(key); foreach (Biome item in replacementMap[key]) { dictionary[item] = new ReplacementAffinity(input, baseAffinities[item], config.affinity.Get(item)); } } return dictionary; } private ReplacementAffinity.Input ComputeInput(Biome biome) { MutableTileData mutableTileData = tiling.MarkConnectedComponents((Tile tile) => flags[tile].IsLand && biomes[tile] == biome); Dictionary dictionary = new Dictionary(); foreach (Tile tile in tiling.Tiles) { int num = mutableTileData[tile]; if (num != -1) { if (!dictionary.ContainsKey(num)) { dictionary[num] = 0.0; } dictionary[num] += tile.Area; } } return new ReplacementAffinity.Input { boundaryProximity = tiling.DistanceTransform((Tile tile) => tile.GetNeighbors().Any((Tile neighbor) => flags[neighbor].IsWater || biomes[neighbor] != biome), (Tile tile) => flags[tile].IsLand && biomes[tile] == biome), patchIds = mutableTileData, patchRadii = dictionary.ToDictionary((KeyValuePair pair) => pair.Key, (KeyValuePair pair) => Rmath.Sqrt(pair.Value)) }; } private Dictionary> ComputeScores(Dictionary> replacementMap, Dictionary affinities) { Dictionary> dictionary = new Dictionary>(); double randomness = Rmath.Max(0.01, config.randomness); foreach (Biome sourceBiome in replacementMap.Keys) { Dictionary> dictionary2 = new Dictionary>(); foreach (Biome targetBiome in replacementMap[sourceBiome]) { if (!replacementConfig.biomes.Get(targetBiome).IsTrivial) { RandomIndexer indexer = random.Indexer($"AffinityRandomization/{targetBiome}"); dictionary2[targetBiome] = (Tile tile) => affinities[targetBiome].Get(tile) + indexer.Single(tile.Id).Range(randomness); } } Dictionary> dictionary3 = BiomeAffinityNormalization.Normalize(config.normalization, tiling, (Tile tile) => flags[tile].IsLand && biomes[tile] == sourceBiome, dictionary2); foreach (Biome key in dictionary3.Keys) { dictionary[key] = dictionary3[key]; } } return dictionary; } private Dictionary ComputeOrigins(Dictionary> scores) { Dictionary dictionary = new Dictionary(); foreach (Biome key in scores.Keys) { dictionary[key] = tiling.FindLocalMaxima(scores[key], config.originSpacing); } return dictionary; } } [Serializable] public struct BiomePlacementConfig { public double weight; public SingleBiomePlacement.Config scoring; public bool IsTrivial => weight <= 0.0; } [Producer(new string[] { "Biomes/CompetitivePlacement" })] [Fork("Region")] public class CompetitiveBiomePlacementStage : IGeneratorStage> { [Serializable] public struct Config { public enum SelectionMethod { Proportional, MostUrgent } public double limit; public double overfillNeighborBonus; public SelectionMethod selectionMethod; public AllBiomeConfig biomes; } public const string OUTPUT = "Biomes/CompetitivePlacement"; [Lane] public Region region; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Base")] public TileData flags; [Resource("Biomes/CompetitivePlacement/Scores")] public Dictionary> scores; [Resource("Biomes/CompetitivePlacement/Origins")] public Dictionary origins; [Resource("Biomes/ConditionalPlacement")] public TileData biomes; public TileData Compute() { MutableTileData result = biomes.Copy(); List<(Biome, BiomePlacementConfig)> nonTrivialConfigs = GetNonTrivialConfigs(); switch (nonTrivialConfigs.Count) { case 0: if (tiling.Tiles.Any((Tile tile) => result[tile] == Biome.None)) { Logger.LogWarning($"No viable biome placement config found for region {region}"); } break; case 1: FillWithSingleBiome(nonTrivialConfigs[0].Item1, result); break; default: PlaceBiomesOnLand(result, nonTrivialConfigs); ExpandBiomes(result, nonTrivialConfigs); FillEmptyBiomesByAffinity(result); break; } return result; } private List<(Biome, BiomePlacementConfig)> GetNonTrivialConfigs() { return (from p in config.biomes.All() where !p.Item2.IsTrivial select p).ToList(); } private void FillWithSingleBiome(Biome biome, MutableTileData result) { foreach (Tile tile in tiling.Tiles) { if (result[tile] == Biome.None) { result[tile] = biome; } } } private void PlaceBiomesOnLand(MutableTileData result, List<(Biome, BiomePlacementConfig)> configs) { TileSet tileSet = new TileSet(tiling); foreach (Tile tile3 in tiling.Tiles) { if (flags[tile3].IsLand && result[tile3] == Biome.None) { tileSet.Add(tile3); } } Dictionary dictionary = new Dictionary(); Dictionary dictionary2 = new Dictionary(); foreach (var config in configs) { Biome item = config.Item1; BiomePlacementConfig item2 = config.Item2; dictionary[item] = item2.weight; dictionary2[item] = new SingleBiomePlacement(item, item2.scoring, scores[item], origins[item], tileSet); } switch (this.config.selectionMethod) { case Config.SelectionMethod.Proportional: { SimpleBiomeSelector simpleBiomeSelector = new SimpleBiomeSelector(tileSet, dictionary, this.config.limit); Biome biome; Tile tile2; while (tileSet.Count > 0 && simpleBiomeSelector.Next(out biome) && dictionary2[biome].Next(out tile2)) { result[tile2] = biome; } break; } case Config.SelectionMethod.MostUrgent: { BiomeSelector biomeSelector = new BiomeSelector(tiling, tileSet, dictionary, scores, this.config.limit); if (!biomeSelector.First(out var selectedBiome)) { break; } Tile tile; while (tileSet.Count > 0 && dictionary2[selectedBiome].Next(out tile)) { result[tile] = selectedBiome; if (!biomeSelector.Next(in tile, out selectedBiome)) { break; } } break; } default: throw new ArgumentOutOfRangeException(); } } private void ExpandBiomes(MutableTileData result, List<(Biome, BiomePlacementConfig)> configs) { TileSet freeTiles = new TileSet(tiling); foreach (Tile tile in tiling.Tiles) { if (result[tile] == Biome.None) { freeTiles.Add(tile); } } Dictionary dictionary = new Dictionary(); foreach (var config in configs) { Biome biome = config.Item1; TileData tileData = tiling.DistanceTransform((Tile tile) => tile.GetNeighbors().Any((Tile neighbor) => result[neighbor] == biome), (Tile tile) => freeTiles.Contains(tile)); foreach (Tile item in freeTiles.Unordered()) { InertFloat inertFloat = tileData[item]; if ((double)inertFloat < double.MaxValue && (!dictionary.ContainsKey(item.Id) || (double)inertFloat < dictionary[item.Id].Item2)) { dictionary[item.Id] = (biome, inertFloat); } } } foreach (int key in dictionary.Keys) { result[tiling.GetTile(key)] = dictionary[key].Item1; } } private void FillEmptyBiomesByAffinity(MutableTileData result) { HashSet hashSet = new HashSet(); foreach (Tile tile in tiling.Tiles) { if (result[tile] != 0) { continue; } foreach (Tile neighbor in tile.GetNeighbors()) { hashSet.Add(result[neighbor]); } Biome biome = Biome.None; double num = double.MinValue; foreach (Biome key in scores.Keys) { InertFloat inertFloat = scores[key][tile]; if (hashSet.Contains(key)) { inertFloat = (double)inertFloat * (1.0 + config.overfillNeighborBonus); } if ((double)inertFloat > num) { biome = key; num = inertFloat; } } hashSet.Clear(); result[tile] = biome; } } } [Producer(new string[] { "Biomes/CompetitivePlacement/Scores", "Biomes/CompetitivePlacement/Origins" })] [Fork("Region")] public class CompetitiveBiomeScoreStage : IGeneratorStage<(Dictionary>, Dictionary)> { [Serializable] public struct Config { public BiomeAffinityNormalization.Config normalization; public double randomness; public double originSpacing; } public const string SCORES = "Biomes/CompetitivePlacement/Scores"; public const string ORIGINS = "Biomes/CompetitivePlacement/Origins"; [Random("Biomes/CompetitiveScoring")] public RandomDirectory random; [LaneConfig] public Config config; [LaneConfig] public CompetitiveBiomePlacementStage.Config placementConfig; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Biomes/ConditionalPlacement")] public TileData biomes; [Resource("Biomes/Affinities")] public Dictionary affinities; public (Dictionary>, Dictionary) Compute() { Dictionary> dictionary = ComputeScores(); Dictionary item = ComputeOrigins(dictionary); return (dictionary, item); } private Dictionary> ComputeScores() { double randomness = Rmath.Max(0.01, config.randomness); Dictionary> dictionary = new Dictionary>(); foreach (var item in placementConfig.biomes.All()) { var (biome, biomePlacementConfig) = item; if (!biomePlacementConfig.IsTrivial) { RandomIndexer indexer = random.Indexer($"AffinityRandomization/{biome}"); dictionary[biome] = (Tile tile) => affinities[biome].Get(tile) + indexer.Single(tile.Id).Range(randomness); } } return BiomeAffinityNormalization.Normalize(config.normalization, tiling, (Tile tile) => biomes[tile] == Biome.None, dictionary); } private Dictionary ComputeOrigins(Dictionary> scores) { Dictionary dictionary = new Dictionary(); foreach (Biome key in scores.Keys) { dictionary[key] = tiling.FindLocalMaxima(scores[key], config.originSpacing); } return dictionary; } } [Producer(new string[] { "Biomes/ConditionalPlacement" })] [Fork("Region")] public class ConditionalBiomePlacementStage : IGeneratorStage> { [Serializable] public struct Config { public enum MountainCriterion { Simple, Scoring } public MountainCriterion mountainCriterion; public double oceanDepth; public double oceanCoastalProximity; public double mountainScoreThreshold; public MountainUtility.MountainScoringConfig mountainScore; public double mountainScoreTemp; } public const string OUTPUT = "Biomes/ConditionalPlacement"; [Lane] public Region region; [Config] public CommonConfig commonConfig; [LaneConfig] public Config config; [Resource("Regions/Tiling")] public Tiling tiling; [Resource("Flags/Base")] public TileData flags; [Resource("CoastalProximity")] public TileData coastalProximity; [Resource("Heights/SettledHeightMap")] public TileData heights; [Resource("Heights/Roughness")] public TileData roughness; [Resource("Heights/RoughnessAnalyzer")] public RoughnessAnalyzer roughnessAnalyzer; public TileData Compute() { return tiling.CreateData(CalculateBiome); } private Biome CalculateBiome(Tile tile) { if (IsOcean(tile)) { return Biome.Ocean; } if (IsMountain(tile)) { return Biome.Mountain; } return Biome.None; } private bool IsOcean(Tile tile) { if (region == Region.SouthPole) { return false; } if (tile.Center.Length > commonConfig.worldSize) { return true; } if (!flags[tile].IsOcean) { return false; } if ((double)heights[tile] > config.oceanDepth) { return false; } if ((double)coastalProximity[tile] < config.oceanCoastalProximity) { return false; } return true; } private bool IsMountain(Tile tile) { if (region != 0) { return false; } return config.mountainCriterion switch { Config.MountainCriterion.Simple => (double)heights[tile] >= commonConfig.mountainHeight, Config.MountainCriterion.Scoring => MountainUtility.NormalizedMountainScore(heights[tile], commonConfig.mountainHeight, roughness[tile], roughnessAnalyzer, config.mountainScore, config.mountainScoreTemp) >= config.mountainScoreThreshold, _ => throw new ArgumentOutOfRangeException(), }; } } } namespace Riverheim.World.Biomes.Competitive { public class BiomeAffinity { [Serializable] public struct NoiseConfig { public double scale; public double magnitude; } [Serializable] public record Config { public double bias; public NoiseConfig noise; public StaticArray radialDistance; public StaticArray travelDistance; public StaticArray relativeTravelDistance; public StaticArray coastalDistance; public StaticArray riverDistance; public StaticArray height; public StaticArray roughness; public StaticArray angle; public double riverSourceBias; } private readonly CommonConfig commonConfig; private readonly Config config; private readonly NoiseField noise; private readonly PiecewiseCurve heightCurve; private readonly PiecewiseCurve radialDistanceCurve; private readonly PiecewiseCurve travelDistanceCurve; private readonly PiecewiseCurve relativeTravelDistanceCurve; private readonly PiecewiseCurve coastalDistanceCurve; private readonly PiecewiseCurve riverDistanceCurve; private readonly PiecewiseCurve roughnessCurve; private readonly PiecewiseCurve angleCurve; private readonly TileData flags; private readonly TileData heights; private readonly TileData travelDistance; private readonly TileData coastalProximity; private readonly TileData riverProximity; private readonly TileData rivers; private readonly TileData roughness; public BiomeAffinity(RandomDirectory random, CommonConfig commonConfig, Config config, TileData flags, TileData heights, TileData travelDistance, TileData coastalProximity, TileData riverProximity, TileData rivers, TileData roughness) { if ((object)config == null) { config = new Config(); } this.commonConfig = commonConfig; this.config = config; this.flags = flags; this.heights = heights; this.travelDistance = travelDistance; this.coastalProximity = coastalProximity; this.riverProximity = riverProximity; this.rivers = rivers; this.roughness = roughness; if (config.noise.magnitude > 0.0) { noise = random.Noise2D("GradientNoise", config.noise.scale); } heightCurve = new PiecewiseCurve(config.height); radialDistanceCurve = new PiecewiseCurve(config.radialDistance); travelDistanceCurve = new PiecewiseCurve(config.travelDistance); relativeTravelDistanceCurve = new PiecewiseCurve(config.relativeTravelDistance); coastalDistanceCurve = new PiecewiseCurve(config.coastalDistance); riverDistanceCurve = new PiecewiseCurve(config.riverDistance); roughnessCurve = new PiecewiseCurve(config.roughness); angleCurve = new PiecewiseCurve(config.angle); } public double Get(Tile tile) { double bias = config.bias; bias += heightCurve.Eval(heights[tile]); bias += radialDistanceCurve.Eval(tile.Center.Length / commonConfig.worldSize); bias += travelDistanceCurve.Eval(travelDistance[tile]); bias += relativeTravelDistanceCurve.Eval((double)travelDistance[tile] / commonConfig.worldSize); bias += coastalDistanceCurve.Eval(flags[tile].IsLand ? coastalProximity[tile] : ((InertFloat)0.0)); bias += riverDistanceCurve.Eval(riverProximity[tile]); bias += roughnessCurve.Eval(roughness[tile].variance); if (!angleCurve.IsTrivial) { bias += angleCurve.Eval(180.0 / Math.PI * Rmath.Atan2(tile.Center.x, Rmath.Abs(tile.Center.y))); } if (rivers[tile].IsRiverTermination()) { bias += config.riverSourceBias; } if (config.noise.magnitude > 0.0) { bias += config.noise.magnitude * noise.Sample(tile.Center); } return bias; } } public static class BiomeAffinityNormalization { [Serializable] public struct Config { public double temperature; public double multiplier; } public static Dictionary> Normalize(Config config, Tiling tiling, Predicate predicate, Dictionary> affinities) { List list = affinities.Keys.ToList(); Dictionary> dictionary = new Dictionary>(); foreach (Biome item in list) { dictionary[item] = tiling.CreateData(); } foreach (Tile tile in tiling.Tiles) { if (!predicate(tile)) { continue; } Dictionary dictionary2 = new Dictionary { { Biome.None, 0.0 } }; foreach (Biome item2 in list) { dictionary2[item2] = affinities[item2](tile); } Dictionary dictionary3 = ComputeProbabilities(dictionary2, config.temperature); foreach (Biome item3 in list) { dictionary[item3][tile] = config.multiplier * dictionary3[item3]; } } return ((IEnumerable>>)dictionary).ToDictionary((Func>, Biome>)((KeyValuePair> kv) => kv.Key), (Func>, TileData>)((KeyValuePair> kv) => kv.Value)); } private static Dictionary ComputeProbabilities(Dictionary weights, double temperature) { List> list = weights.ToList(); double[] array = Functions.Softmax(list.Select((KeyValuePair kv) => kv.Value).ToArray(), temperature); Dictionary dictionary = new Dictionary(); for (int i = 0; i < array.Length; i++) { dictionary[list[i].Key] = array[i]; } return dictionary; } } public class BiomeSelector { private readonly IntHeap heap; private readonly Dictionary> scores; private readonly Dictionary leftToPlace; private readonly Dictionary totalScores; public BiomeSelector(Tiling tiling, TileSet freeTiles, Dictionary weights, Dictionary> scores, double limit) { double num = (double)freeTiles.Count * limit; double num2 = weights.Sum((KeyValuePair kv) => kv.Value); this.scores = scores; heap = new IntHeap(HeapType.Max); leftToPlace = new Dictionary(); totalScores = new Dictionary(); TileList tileList = new TileList(tiling); foreach (Tile item in freeTiles.ConsistentOrdering()) { tileList.Add(item); } foreach (Biome key in weights.Keys) { leftToPlace[key] = Rmath.FloorToInt(num * weights[key] / num2); totalScores[key] = 0.0; foreach (Tile item2 in tileList) { totalScores[key] += scores[key][item2]; } heap.AddOrUpdate((int)key, (double)leftToPlace[key] / totalScores[key]); } } public bool First(out Biome selectedBiome) { if (heap.Count == 0) { selectedBiome = Biome.None; return false; } selectedBiome = (Biome)heap.Peek(); leftToPlace[selectedBiome]--; if (leftToPlace[selectedBiome] <= 0) { heap.Remove((int)selectedBiome); } return true; } public bool Next(in Tile tile, out Biome selectedBiome) { if (heap.Count == 0) { selectedBiome = Biome.None; return false; } Biome[] array = totalScores.Keys.ToArray(); foreach (Biome biome in array) { totalScores[biome] -= scores[biome][tile]; if (heap.Contains((int)biome)) { heap.AddOrUpdate((int)biome, (double)leftToPlace[biome] / totalScores[biome]); } } selectedBiome = (Biome)heap.Peek(); leftToPlace[selectedBiome]--; if (leftToPlace[selectedBiome] <= 0) { heap.Remove((int)selectedBiome); } return true; } } public class PatchHeap { public delegate(Tile, double) GetOrigin(int patchId); private readonly IntHeap heads; private readonly Dictionary patches; private readonly Dictionary patchWeights; private readonly GetOrigin getOrigin; private int uninitCount; private int tileCount; public PatchHeap(int patchCount, GetOrigin getOrigin) { heads = new IntHeap(HeapType.Max); patches = new Dictionary(); patchWeights = new Dictionary(); uninitCount = patchCount; tileCount = uninitCount; for (int i = 0; i < patchCount; i++) { heads.AddOrUpdate(i, getOrigin(i).Item2); } this.getOrigin = getOrigin; } public int Count() { return tileCount; } public bool Contains(int patchId, Tile tile, out double weight) { return GetPatch(patchId).Contains(tile, out weight); } public (int, Tile) Pop() { int num = heads.Peek(); Tile item = GetPatch(num).Pop(); UpdateHead(num); tileCount--; return (num, item); } public void AddOrUpdate(int patchId, Tile tile, double weight, double patchWeight) { if (GetPatch(patchId).AddOrUpdate(tile, weight)) { tileCount++; } patchWeights[patchId] = patchWeight; UpdateHead(patchId); } private TileHeap GetPatch(int patchId) { if (!patches.ContainsKey(patchId)) { var (init, weight) = getOrigin(patchId); patches[patchId] = TileHeap.InitMax(init, weight); patchWeights[patchId] = 0.0; uninitCount--; } return patches[patchId]; } private void UpdateHead(int patchId) { if (patches[patchId].Count == 0) { heads.Remove(patchId); return; } patches[patchId].Peek(out var weight); heads.AddOrUpdate(patchId, weight * patchWeights[patchId]); } } public class SimpleBiomeSelector { private readonly IntHeap heap; private readonly Dictionary currentCounts; private readonly Dictionary targetCounts; public SimpleBiomeSelector(TileSet freeTiles, Dictionary weights, double usage) { double num = (double)freeTiles.Count * usage; double num2 = weights.Sum((KeyValuePair kv) => kv.Value); currentCounts = new Dictionary(); targetCounts = new Dictionary(); heap = new IntHeap(HeapType.Max); foreach (Biome key in weights.Keys) { currentCounts[key] = 0; targetCounts[key] = Rmath.FloorToInt(num * weights[key] / num2); if (targetCounts[key] > 0) { heap.AddOrUpdate((int)key, 1.0); } } } public bool Next(out Biome biome) { if (heap.Count == 0) { biome = Biome.None; return false; } biome = (Biome)heap.Peek(); currentCounts[biome]++; if (currentCounts[biome] >= targetCounts[biome]) { heap.Remove((int)biome); } else { heap.AddOrUpdate((int)biome, (double)(targetCounts[biome] - currentCounts[biome]) / (double)targetCounts[biome]); } return true; } } public class SingleBiomePlacement { [Serializable] public struct Config { public double cohesionBonusAdd; public double cohesionBonusMul; public double targetPatchSize; public double patchSizeTemp; } private readonly PatchHeap heap; private readonly Biome biome; private readonly Config config; private readonly TileData affinity; private readonly TileSet freeTiles; private readonly Dictionary patchAreas; public SingleBiomePlacement(Biome biome, Config config, TileData affinity, TileList seedingPoints, TileSet freeTiles) { SingleBiomePlacement singleBiomePlacement = this; this.biome = biome; this.config = config; this.affinity = affinity; this.freeTiles = freeTiles; heap = new PatchHeap(seedingPoints.Count, delegate(int patchId) { Tile tile = seedingPoints[patchId]; return (tile, singleBiomePlacement.affinity[tile]); }); patchAreas = new Dictionary(); } public bool Next(out Tile tile) { while (heap.Count() > 0) { var (num, tile2) = heap.Pop(); if (!freeTiles.Contains(tile2)) { continue; } tile = tile2; freeTiles.Remove(tile); patchAreas.TryAdd(num, 0.0); patchAreas[num] += tile.Area; foreach (Tile neighbor in tile.GetNeighbors()) { if (freeTiles.Contains(neighbor)) { double weight; double weight2 = (heap.Contains(num, neighbor, out weight) ? Rmath.Max(0.0, weight + (double)affinity[neighbor] * config.cohesionBonusMul + config.cohesionBonusAdd) : ((double)affinity[neighbor])); heap.AddOrUpdate(num, neighbor, weight2, PatchAreaScore(num)); } } return true; } Logger.LogWarning($"SingleBiomePlacement[{biome}]: no patches left"); tile = default(Tile); return false; } private double PatchAreaScore(int patchId) { double num = ((config.patchSizeTemp != 0.0) ? (1.0 / config.patchSizeTemp) : 1.0); double num2 = config.targetPatchSize * config.targetPatchSize; if (num2 != 0.0) { return Rmath.Exp((0.0 - num) * (patchAreas[patchId] / num2 - 1.0)); } return 1.0; } } } namespace Riverheim.Util { public readonly struct Couple : IEnumerable, IEnumerable, IEquatable> { [CompilerGenerated] private sealed class d__12 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private T <>2__current; public Couple <>4__this; private int 5__2; T IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__12(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = 0; break; case 1: <>1__state = -1; 5__2++; break; } if (5__2 < <>4__this.Count) { <>2__current = <>4__this[5__2]; <>1__state = 1; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private readonly T v1; private readonly T v2; public int Count { get; } public T this[int index] { get { if (index >= Count) { throw new IndexOutOfRangeException(); } return index switch { 0 => v1, 1 => v2, _ => throw new IndexOutOfRangeException(), }; } } public Couple(T v1) { this.v1 = v1; v2 = default(T); Count = 1; } public Couple(T v1, T v2) { this.v1 = v1; this.v2 = v2; Count = 2; } public static Couple MakeCouple(IList list) { return list.Count switch { 0 => default(Couple), 1 => new Couple(list[0]), 2 => new Couple(list[0], list[1]), _ => throw new ArgumentException("Couple must have 0, 1 or 2 elements!"), }; } public Couple Transform(Func transform) { return Count switch { 0 => default(Couple), 1 => new Couple(transform(v1)), 2 => new Couple(transform(v1), transform(v2)), _ => throw new Exception(), }; } public Couple Transform(Func transform) { return Count switch { 0 => default(Couple), 1 => new Couple(transform(0)), 2 => new Couple(transform(0), transform(1)), _ => throw new Exception(), }; } [IteratorStateMachine(typeof(Couple<>.d__12))] public IEnumerator GetEnumerator() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__12(0) { <>4__this = this }; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public bool Equals(Couple other) { if (EqualityComparer.Default.Equals(v1, other.v1) && EqualityComparer.Default.Equals(v2, other.v2)) { return Count == other.Count; } return false; } public override bool Equals(object obj) { if (obj is Couple other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(v1, v2, Count); } } public static class CoupleExtensions { [CompilerGenerated] private sealed class d__0 : IEnumerable>, IEnumerable, IEnumerator>, IEnumerator, IDisposable { private int <>1__state; private Couple <>2__current; private int <>l__initialThreadId; private IList items; public IList <>3__items; private int 5__2; private int 5__3; Couple IEnumerator>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = default(Couple); <>1__state = 1; return true; case 1: <>1__state = -1; 5__2 = 0; break; case 2: <>1__state = -1; 5__3 = 5__2 + 1; goto IL_00d7; case 3: { <>1__state = -1; 5__3++; goto IL_00d7; } IL_00d7: if (5__3 < items.Count) { <>2__current = new Couple(items[5__2], items[5__3]); <>1__state = 3; return true; } 5__2++; break; } if (5__2 < items.Count) { <>2__current = new Couple(items[5__2]); <>1__state = 2; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator> IEnumerable>.GetEnumerator() { d__0 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__0(0); } d__.items = <>3__items; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable>)this).GetEnumerator(); } } [IteratorStateMachine(typeof(d__0<>))] public static IEnumerable> AllPairings(this IList items) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__0(-2) { <>3__items = items }; } } public struct Direction : IEquatable { private double angleValue; public double Angle { get { return angleValue; } set { angleValue = Clamped(value); } } private static double Clamped(double angle) { return angle - Math.PI * 2.0 * (double)Rmath.FloorToInt(0.5 * (1.0 + angle / Math.PI)); } public Direction(double angle) { angleValue = Clamped(angle); } public Direction(Rfloat2 v) { angleValue = Clamped(Rmath.Atan2(v.y, v.x)); } public static Direction Average(Direction lhs, Direction rhs) { return new Direction((lhs.Angle + rhs.Angle) / 2.0); } public Direction Rotate(double angle) { return new Direction(Angle + angle); } public readonly Rfloat2 ToVector() { return new Rfloat2(Rmath.Cos(angleValue), Rmath.Sin(angleValue)); } public static double operator -(Direction lhs, Direction rhs) { return Clamped(lhs.Angle - rhs.Angle); } public static Direction operator ~(Direction d) { return new Direction(d.angleValue + Math.PI); } public bool Equals(Direction other) { return Rmath.Abs(angleValue - other.angleValue) < 1E-06; } public override bool Equals(object obj) { if (obj is Direction other) { return Equals(other); } return false; } public static bool operator ==(Direction d1, Direction d2) { return d1.Equals(d2); } public static bool operator !=(Direction d1, Direction d2) { return !(d1 == d2); } public override int GetHashCode() { return angleValue.GetHashCode(); } } public class IndexedSet : IEnumerable, IEnumerable { private readonly List items = new List(); private readonly Dictionary index = new Dictionary(); public int Count => items.Count; public bool Contains(T item) { return index.ContainsKey(item); } public void Add(T item) { if (!Contains(item)) { index[item] = items.Count; items.Add(item); } } public void Remove(T item) { if (Contains(item)) { int num = index[item]; if (num != items.Count - 1) { items[num] = items[items.Count - 1]; index[items[num]] = num; } items.RemoveAt(items.Count - 1); index.Remove(item); } } public T Get(int idx) { return items[idx]; } public IEnumerator GetEnumerator() { return items.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return items.GetEnumerator(); } } public enum HeapType { Min, Max } public class IntHeap { private readonly bool maxHeap; private readonly List items = new List(); private readonly Dictionary weights = new Dictionary(); private readonly Dictionary indices = new Dictionary(); public int Count => items.Count; public IntHeap(HeapType type) { maxHeap = type == HeapType.Max; } public bool Contains(int item) { return indices.ContainsKey(item); } public bool Contains(int item, out double weight) { return weights.TryGetValue(item, out weight); } public bool AddOrUpdate(int item, double weight) { if (indices.TryGetValue(item, out var value)) { weights[item] = weight; HeapifyUp(value); HeapifyDown(value); return false; } indices[item] = items.Count; items.Add(item); weights[item] = weight; HeapifyUp(items.Count - 1); return true; } public bool Remove(int item) { if (!indices.TryGetValue(item, out var value)) { return false; } RemoveAt(value); return true; } public int Peek() { if (items.Count == 0) { throw new InvalidOperationException("Heap is empty"); } return items[0]; } public int Peek(out double weight) { if (items.Count == 0) { throw new InvalidOperationException("Heap is empty"); } weight = weights[items[0]]; return items[0]; } public int Pop() { if (items.Count == 0) { throw new InvalidOperationException("Heap is empty"); } return RemoveAt(0); } public int Pop(out double weight) { if (items.Count == 0) { throw new InvalidOperationException("Heap is empty"); } weight = weights[items[0]]; return RemoveAt(0); } private int RemoveAt(int index) { int num = items[index]; Swap(index, Count - 1); weights.Remove(num); indices.Remove(num); items.RemoveAt(Count - 1); if (index < items.Count) { HeapifyDown(index); HeapifyUp(index); } return num; } private void HeapifyUp(int index) { while (index > 0) { int num = Parent(index); if (!IsBefore(num, index)) { Swap(num, index); index = num; continue; } break; } } private void HeapifyDown(int index) { while (true) { int num = Left(index); int num2 = Right(index); int num3 = index; if (num < Count && IsBefore(num, num3)) { num3 = num; } if (num2 < Count && IsBefore(num2, num3)) { num3 = num2; } if (num3 != index) { Swap(index, num3); index = num3; continue; } break; } } private bool IsBefore(int lhs, int rhs) { int num = items[lhs]; int num2 = items[rhs]; double num3 = weights[num]; double num4 = weights[num2]; if (!maxHeap) { if (!(num3 < num4)) { if (!(num3 > num4)) { return num < num2; } return false; } return true; } if (!(num3 > num4)) { if (!(num3 < num4)) { return num > num2; } return false; } return true; } private void Swap(int i, int j) { indices[items[i]] = j; indices[items[j]] = i; List list = items; List list2 = items; int value = items[j]; int value2 = items[i]; list[i] = value; list2[j] = value2; } private static int Parent(int index) { return (index - 1) / 2; } private static int Left(int index) { return index * 2 + 1; } private static int Right(int index) { return index * 2 + 2; } } public struct Rfloat4 { public readonly double x; public readonly double y; public readonly double z; public readonly double w; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rfloat4(double x, double y, double z, double w) { this.x = x; this.y = y; this.z = z; this.w = w; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Dot(Rfloat4 lhs, Rfloat4 rhs) { return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z + lhs.w * rhs.w; } } public struct Rfloat4X4 { private readonly double m11; private readonly double m12; private readonly double m13; private readonly double m14; private readonly double m21; private readonly double m22; private readonly double m23; private readonly double m24; private readonly double m31; private readonly double m32; private readonly double m33; private readonly double m34; private readonly double m41; private readonly double m42; private readonly double m43; private readonly double m44; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Rfloat4X4(double m11, double m12, double m13, double m14, double m21, double m22, double m23, double m24, double m31, double m32, double m33, double m34, double m41, double m42, double m43, double m44) { this.m11 = m11; this.m12 = m12; this.m13 = m13; this.m14 = m14; this.m21 = m21; this.m22 = m22; this.m23 = m23; this.m24 = m24; this.m31 = m31; this.m32 = m32; this.m33 = m33; this.m34 = m34; this.m41 = m41; this.m42 = m42; this.m43 = m43; this.m44 = m44; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat4 operator *(Rfloat4 lhs, Rfloat4X4 rhs) { return new Rfloat4(lhs.x * rhs.m11 + lhs.y * rhs.m21 + lhs.z * rhs.m31 + lhs.w * rhs.m41, lhs.x * rhs.m12 + lhs.y * rhs.m22 + lhs.z * rhs.m32 + lhs.w * rhs.m42, lhs.x * rhs.m13 + lhs.y * rhs.m23 + lhs.z * rhs.m33 + lhs.w * rhs.m43, lhs.x * rhs.m14 + lhs.y * rhs.m24 + lhs.z * rhs.m34 + lhs.w * rhs.m44); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat4X4 operator /(Rfloat4X4 mat, double d) { return new Rfloat4X4(mat.m11 / d, mat.m12 / d, mat.m13 / d, mat.m14 / d, mat.m21 / d, mat.m22 / d, mat.m23 / d, mat.m24 / d, mat.m31 / d, mat.m32 / d, mat.m33 / d, mat.m34 / d, mat.m41 / d, mat.m42 / d, mat.m43 / d, mat.m44 / d); } } public readonly struct PiecewiseCurve { private readonly Rfloat2[] points; public bool IsTrivial => points.Length == 0; public PiecewiseCurve(IEnumerable points) { this.points = points.Select((Func)((Rpoint2 p) => p)).ToArray(); for (int i = 0; i < this.points.Length - 1; i++) { if (this.points[i].x > this.points[i + 1].x) { throw new ArgumentException(); } } } public PiecewiseCurve(IEnumerable points) { this.points = points.ToArray(); for (int i = 0; i < this.points.Length - 1; i++) { if (this.points[i].x > this.points[i + 1].x) { throw new ArgumentException(); } } } public double Eval(double x) { if (points.Length == 0) { return 0.0; } if (x <= points[0].x) { return points[0].y; } for (int i = 1; i < points.Length; i++) { if (!(x > points[i].x)) { Rfloat2 rfloat = points[i]; Rfloat2 rfloat2 = points[i - 1]; return Rmath.Lerp(Rmath.InverseLerp(x, rfloat2.x, rfloat.x), rfloat2.y, rfloat.y); } } return points[points.Length - 1].y; } } public static class ProfileUtility { public static class SmoothRamp { [Serializable] public struct Config { public double slope; public double reach; public double power; } public static double Profile(double distance, in Config config) { double reach = config.reach; double power = config.power; return config.slope * (Rmath.Pow(Rmath.Pow(distance, power) + Rmath.Pow(reach, power), 1.0 / power) - reach); } public static double Inverse(double height, in Config config) { if (Rmath.Sign(config.slope) != Rmath.Sign(height)) { return 0.0; } double reach = config.reach; double power = config.power; return Rmath.Pow(Rmath.Pow(height / config.slope + reach, power) - Rmath.Pow(reach, power), 1.0 / power); } } public static class Saturated { [Serializable] public struct Config { public double reach; public double limit; public double pow; } private static double nlog2 = 0.0 - Rmath.Log(2.0); public static double Profile(double distance, in Config config) { return config.limit * (1.0 - Rmath.Exp(nlog2 * Rmath.Pow(distance / config.reach, config.pow))); } public static double Inverse(double height, in Config config) { if (!Rmath.Within(height, Rmath.Range.From(0.0, config.limit))) { return 0.0; } return config.reach * Rmath.Pow(Rmath.Log(1.0 - height / config.limit) / nlog2, 1.0 / config.pow); } } public static class Rational { [Serializable] public struct Config { public double slope; public double scale; public Config Inverted { get { Config result = default(Config); result.slope = 0.0 - slope; result.scale = scale; return result; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Profile(double distance, in Config config) { return config.slope * distance * distance / (distance + config.scale); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Derivative(double distance, in Config config) { double num = distance + config.scale; return config.slope * distance * (distance + 2.0 * config.scale) / (num * num); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Inverse(double height, in Config config) { double value = height * height + 4.0 * height * config.slope * config.scale; return (height + (double)Rmath.Sign(config.slope) * Rmath.Sqrt(value)) / (2.0 * config.slope); } } public static class BiasedRational { [Serializable] public struct Config { public double slope; public double w; public double scale; public static Config FromMetrics(double width, double depth, double steepness) { double num = width / depth; Config result = default(Config); result.slope = steepness; result.w = num * (steepness * num - 1.0); result.scale = depth; return result; } public static Config Lerp(double t, in Config lhs, in Config rhs) { Config result = default(Config); result.slope = Rmath.Lerp(t, lhs.slope, rhs.slope); result.w = Rmath.Lerp(t, lhs.w, rhs.w); result.scale = Rmath.Lerp(t, lhs.scale, rhs.scale); return result; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Profile(double distance, in Config config) { double num = distance / config.scale; return config.scale * (config.slope * num * num / (num + config.w) - 1.0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Derivative(double distance, in Config config) { double num = distance / config.scale; return config.slope * num * (num + 2.0 * config.w) / ((num + config.w) * (num + config.w)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Inverse(double height, in Config config) { double num = (height + config.scale) / config.scale; return config.scale / (2.0 * config.slope) * (num + Rmath.Sqrt(num * num + 4.0 * num * config.slope * config.w)); } } } public readonly struct Rfloat3 { private const MethodImplOptions INLINE = MethodImplOptions.AggressiveInlining; public readonly double x; public readonly double y; public readonly double z; public static Rfloat3 Zero => new Rfloat3(0.0, 0.0, 0.0); public double Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Rmath.Sqrt(x * x + y * y + z * z); } } public Rfloat3 Normalized { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { double length = Length; return new Rfloat3(x / length, y / length, z / length); } } public Rfloat3(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat3 operator *(Rfloat3 vec, double scalar) { return new Rfloat3(vec.x * scalar, vec.y * scalar, vec.z * scalar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat3 operator *(double scalar, Rfloat3 vec) { return new Rfloat3(scalar * vec.x, scalar * vec.y, scalar * vec.z); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat3 operator +(Rfloat3 lhs, Rfloat3 rhs) { return new Rfloat3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z); } } [Serializable] public struct Rpoint2 { public double x; public double y; public Rpoint2(double x, double y) { this.x = x; this.y = y; } public static implicit operator Rpoint2((double x, double y) p) { return new Rpoint2(p.x, p.y); } public static implicit operator Rfloat2(Rpoint2 p) { return new Rfloat2(p.x, p.y); } public static implicit operator InertFloat2(Rpoint2 p) { return (Rfloat2)p; } } [Serializable] public struct StaticArray : IEnumerable, IEnumerable { [CompilerGenerated] private sealed class d__16 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private T <>2__current; public StaticArray <>4__this; private int 5__2; T IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__16(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = 0; break; case 1: <>1__state = -1; 5__2++; break; } if (5__2 < <>4__this.count) { <>2__current = <>4__this[5__2]; <>1__state = 1; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const int CAPACITY = 8; public int count; public T p0; public T p1; public T p2; public T p3; public T p4; public T p5; public T p6; public T p7; public T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (index < 0 || index >= count) { throw new IndexOutOfRangeException(); } return index switch { 0 => p0, 1 => p1, 2 => p2, 3 => p3, 4 => p4, 5 => p5, 6 => p6, 7 => p7, _ => throw new IndexOutOfRangeException(), }; } [MethodImpl(MethodImplOptions.AggressiveInlining)] set { if (index < 0 || index >= count) { throw new IndexOutOfRangeException(); } switch (index) { case 0: p0 = value; break; case 1: p1 = value; break; case 2: p2 = value; break; case 3: p3 = value; break; case 4: p4 = value; break; case 5: p5 = value; break; case 6: p6 = value; break; case 7: p7 = value; break; default: throw new IndexOutOfRangeException(); } } } public StaticArray(params T[] array) { if (array == null) { throw new ArgumentNullException(); } this = new StaticArray(array.Length); switch (array.Length) { case 8: p7 = array[7]; goto case 7; case 7: p6 = array[6]; goto case 6; case 6: p5 = array[5]; goto case 5; case 5: p4 = array[4]; goto case 4; case 4: p3 = array[3]; goto case 3; case 3: p2 = array[2]; goto case 2; case 2: p1 = array[1]; goto case 1; case 1: p0 = array[0]; break; default: throw new IndexOutOfRangeException(); case 0: break; } } public StaticArray(int count = 0) { this = default(StaticArray); if (count > 8) { throw new ArgumentOutOfRangeException("count"); } this.count = count; } public readonly T[] ToArray() { T[] array = new T[count]; switch (count) { case 8: array[7] = p7; goto case 7; case 7: array[6] = p6; goto case 6; case 6: array[5] = p5; goto case 5; case 5: array[4] = p4; goto case 4; case 4: array[3] = p3; goto case 3; case 3: array[2] = p2; goto case 2; case 2: array[1] = p1; goto case 1; case 1: array[0] = p0; break; } return array; } [IteratorStateMachine(typeof(StaticArray<>.d__16))] public IEnumerator GetEnumerator() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__16(0) { <>4__this = this }; } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(T value) { if (count == 8) { throw new IndexOutOfRangeException(); } this[count++] = value; } } } namespace Riverheim.Util.Tilings { public class BarycentricDualTilingBuilder { private readonly DelaunayGraph graph; private readonly TriangleMesh mesh; private readonly double spacingHint; public BarycentricDualTilingBuilder(InertFloat2[] points, double spacingHint) { graph = new DelaunayGraph(points); mesh = new TriangleMesh(graph.Points.ToList(), graph.Triangles.ToList(), graph.Bounds, spacingHint); this.spacingHint = spacingHint; } public Tiling Build() { return new Tiling(spacingHint, BuildTileInfos(graph), new BarycentricDualSpatialIndex(mesh, graph.PrunedCount)); } public TriangleMesh BuildMesh() { return mesh; } private static Tiling.TileInfo[] BuildTileInfos(DelaunayGraph graph) { Tiling.TileInfo[] array = new Tiling.TileInfo[graph.PrunedCount]; Rfloat2[] array2 = graph.Points.ToArray(); double[] barycentricDualAreas = graph.BarycentricDualAreas; for (int i = 0; i < graph.PrunedCount; i++) { array[i] = new Tiling.TileInfo { center = array2[i], area = barycentricDualAreas[i], adjacency = graph.GetAdjacency(i), hopsToBounds = graph.GetHopsToBounds(i) }; } return array; } } public class BarycentricDualSpatialIndex : ITilingSpatialIndex { private readonly TriangleMesh mesh; private readonly int maxTileId; public BarycentricDualSpatialIndex(TriangleMesh mesh, int maxTileId) { this.mesh = mesh; this.maxTileId = maxTileId; } public int GetTileId(Rfloat2 pos) { BarycentricCoordinates bc; int triangleIndex = mesh.GetTriangleIndex(pos, out bc); if (triangleIndex == -1) { return -1; } int closestVertex = mesh.GetClosestVertex(triangleIndex, bc); if (closestVertex >= maxTileId) { return -1; } return closestVertex; } } public class PoissonDiscSampler { private const int K = 10; private readonly RandomSequence random; private readonly double minSpacing; private readonly List points; private readonly RandomSet active; private readonly SquareGrid grid; public PoissonDiscSampler(RandomSequence random, double boundingRadius, double minSpacing) { this.random = random; this.minSpacing = minSpacing; points = new List(); active = new RandomSet(random); grid = InitGrid(boundingRadius, minSpacing); Generate(); } public List GetPoints() { return points; } private static SquareGrid InitGrid(double boundingRadius, double r) { double num = r / Rmath.Sqrt(2.0); int num2 = Rmath.CeilToInt(boundingRadius / num); SquareGrid squareGrid = new SquareGrid(new Rint2(2 * num2, 2 * num2), num, (double)num2 * num * Rfloat2.One); foreach (RectangularGrid.Cell item in squareGrid.All()) { item.Data = -1; } return squareGrid; } private void Generate() { AddPoint(random.Double.Range(0.0 - minSpacing, minSpacing)); while (active.Count > 0) { int num = active.Sample(); bool flag = false; int num2 = 0; while (!flag && num2++ < 10) { flag = TryAddPoint(points[num] + RandomOffset()); } if (!flag) { active.Remove(num); } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void AddPoint(Rfloat2 point) { RectangularGrid.Cell cell = grid.GetCell(point) ?? throw new Exception("point out of bounds"); int count = points.Count; cell.Data = count; active.Add(count); points.Add(point); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool HasPointWithinRange(Rfloat2 point, RectangularGrid.Cell cell) { foreach (RectangularGrid.Cell neighbor in cell.GetNeighbors()) { if (neighbor.Data != -1 && (points[neighbor.Data] - point).SqrLength < minSpacing * minSpacing) { return true; } } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool TryAddPoint(Rfloat2 point) { RectangularGrid.Cell? cell = grid.GetCell(point); if (cell.HasValue) { RectangularGrid.Cell valueOrDefault = cell.GetValueOrDefault(); if (valueOrDefault.Data != -1) { return false; } if (HasPointWithinRange(point, valueOrDefault)) { return false; } AddPoint(point); return true; } return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private Rfloat2 RandomOffset() { double num = minSpacing * minSpacing; double num2 = Rmath.Sqrt(3.0 * num * random.Single.Value + num); double value = random.Single.Range(0.0, Math.PI * 2.0); return new Rfloat2(num2 * Rmath.Cos(value), num2 * Rmath.Sin(value)); } } public class SubtilingMapping { private readonly Tiling parent; private readonly Tiling child; private readonly int[] childToParent; private readonly Dictionary parentToChild; public SubtilingMapping(Tiling parent, Tiling child, int[] childToParent, Dictionary parentToChild) { this.parent = parent; this.child = child; this.childToParent = childToParent; this.parentToChild = parentToChild; } public Tile GetParentTile(Tile childTile) { return parent.GetTile(childToParent[childTile.Id]); } public Tile? GetChildTile(Tile parentTile) { if (!parentToChild.TryGetValue(parentTile.Id, out var value)) { return null; } return child.GetTile(value); } } public readonly struct Tile : IEquatable { public int Id { get; } public Tiling Tiling { get; } public Rfloat2 Center => Tiling.GetCenter(Id); public double Area => Tiling.GetArea(Id); public int HopsToBounds => Tiling.GetHopsToBounds(Id); internal Tile(int id, Tiling tiling) { Id = id; Tiling = tiling; } public IEnumerable GetNeighbors() { return Tiling.GetNeighbors(Id); } public IEnumerable GetNeighbors(int hops) { return Tiling.GetNeighbors(Id, hops); } public IEnumerable GetNeighbors(int hops, Predicate predicate) { return Tiling.GetNeighbors(Id, hops, predicate); } public T Data(TileData data) { return data[this]; } public ref T Data(MutableTileData data) { return ref data[this]; } public static bool operator ==(Tile lhs, Tile rhs) { if (lhs.Tiling.Equals(rhs.Tiling)) { return lhs.Id.Equals(rhs.Id); } return false; } public static bool operator !=(Tile lhs, Tile rhs) { return !(lhs == rhs); } public bool Equals(Tile other) { return this == other; } public override bool Equals(object obj) { if (obj is Tile other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(Id, Tiling); } public override string ToString() { return $"Tile {{ id = {Id}, center = {Center}, hopsToBounds = {HopsToBounds} }}"; } } public class TileData { private readonly T[] storage; public T this[Tile tile] => storage[tile.Id]; internal TileData(T[] storage) { this.storage = storage; } public T GetByVertexId(int id) { return storage[id]; } public MutableTileData Copy() { return new MutableTileData(storage); } public MutableTileData Transform(Func transform) { return new MutableTileData(storage.Select(transform)); } } public class MutableTileData { private readonly T[] storage; public ref T this[Tile tile] => ref storage[tile.Id]; internal MutableTileData(IEnumerable data) { storage = data.ToArray(); } internal MutableTileData(Tiling tiling) { storage = new T[tiling.Count]; } internal MutableTileData(Tiling tiling, Func init) : this(tiling) { foreach (Tile tile in tiling.Tiles) { storage[tile.Id] = init(tile); } } internal MutableTileData(Tiling tiling, T init) : this(tiling) { foreach (Tile tile in tiling.Tiles) { storage[tile.Id] = init; } } public static implicit operator TileData(MutableTileData mutableTileData) { return new TileData(mutableTileData.storage); } public MutableTileData Copy() { return new MutableTileData(storage); } public MutableTileData Transform(Func transform) { return new MutableTileData(storage.Select(transform)); } } public interface ITilingSpatialIndex { int GetTileId(Rfloat2 pos); } public class Tiling { public struct TileInfo { public InertFloat2 center; public InertFloat area; public int[] adjacency; public int hopsToBounds; } [CompilerGenerated] private sealed class d__21 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private Tile <>2__current; private int <>l__initialThreadId; public Tiling <>4__this; private int tileId; public int <>3__tileId; private int[] <>7__wrap1; private int <>7__wrap2; Tile IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__21(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; Tiling tiling = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = tiling.tileInfos[tileId].adjacency; <>7__wrap2 = 0; break; case 1: <>1__state = -1; <>7__wrap2++; break; } if (<>7__wrap2 < <>7__wrap1.Length) { int id = <>7__wrap1[<>7__wrap2]; <>2__current = new Tile(id, tiling); <>1__state = 1; return true; } <>7__wrap1 = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__21 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__21(0) { <>4__this = <>4__this }; } d__.tileId = <>3__tileId; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [CompilerGenerated] private sealed class d__22 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private Tile <>2__current; private int <>l__initialThreadId; private int tileId; public int <>3__tileId; public Tiling <>4__this; private Predicate predicate; public Predicate <>3__predicate; private int hops; public int <>3__hops; private HashSet 5__2; private List 5__3; private List 5__4; private List.Enumerator <>7__wrap4; private int 5__6; Tile IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__22(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } 5__2 = null; 5__3 = null; 5__4 = null; <>7__wrap4 = default(List.Enumerator); <>1__state = -2; } private bool MoveNext() { try { int num = <>1__state; Tiling tiling = <>4__this; if (num != 0) { if (num != 1) { return false; } <>1__state = -3; goto IL_00f3; } <>1__state = -1; 5__2 = new HashSet { tileId }; 5__3 = new List(); 5__4 = new List { tileId }; goto IL_0180; IL_0180: if (hops-- >= 0 && 5__4.Count > 0) { List list = 5__4; List list2 = 5__3; 5__4 = list2; 5__3 = list; 5__4.Clear(); <>7__wrap4 = 5__3.GetEnumerator(); <>1__state = -3; goto IL_015e; } return false; IL_015e: if (<>7__wrap4.MoveNext()) { 5__6 = <>7__wrap4.Current; if (5__6 != tileId) { <>2__current = new Tile(5__6, tiling); <>1__state = 1; return true; } goto IL_00f3; } <>m__Finally1(); <>7__wrap4 = default(List.Enumerator); goto IL_0180; IL_00f3: int[] adjacency = tiling.tileInfos[5__6].adjacency; foreach (int num2 in adjacency) { if ((predicate == null || predicate(new Tile(num2, tiling))) && 5__2.Add(num2)) { 5__4.Add(num2); } } goto IL_015e; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>7__wrap4).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__22 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__22(0) { <>4__this = <>4__this }; } d__.tileId = <>3__tileId; d__.hops = <>3__hops; d__.predicate = <>3__predicate; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [CompilerGenerated] private sealed class d__17 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private Tile <>2__current; private int <>l__initialThreadId; public Tiling <>4__this; private int 5__2; Tile IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__17(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; Tiling tiling = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; 5__2 = 0; break; case 1: <>1__state = -1; 5__2++; break; } if (5__2 < tiling.Count) { <>2__current = new Tile(5__2, tiling); <>1__state = 1; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__17 result; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; result = this; } else { result = new d__17(0) { <>4__this = <>4__this }; } return result; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } public const int TILE_ID_INVALID = -1; private readonly TileInfo[] tileInfos; private readonly ITilingSpatialIndex spatialIndex; private readonly int[] subTilingMapping; public int Count => tileInfos.Length; public double TotalArea { get; } public double Spacing { get; } public IEnumerable Tiles { [IteratorStateMachine(typeof(d__17))] get { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__17(-2) { <>4__this = this }; } } private bool IsSubTiling => subTilingMapping != null; public Tiling(double spacing, IEnumerable tileInfos, ITilingSpatialIndex spatialIndex, int[] subTilingMapping = null) { Spacing = spacing; this.tileInfos = tileInfos.ToArray(); this.spatialIndex = spatialIndex; this.subTilingMapping = subTilingMapping; TotalArea = ComputeTotalArea(); } public Tile GetTile(int id) { return new Tile(id, this); } public Tile? GetTile(Rfloat2 pos) { int tileId = spatialIndex.GetTileId(pos); if (tileId == -1) { return null; } if (!IsSubTiling) { if (tileId >= Count) { return null; } return new Tile(tileId, this); } int num = subTilingMapping[tileId]; if (num == -1) { return null; } return new Tile(num, this); } public Rfloat2 GetCenter(int tileId) { return tileInfos[tileId].center; } public double GetArea(int tileId) { return tileInfos[tileId].area; } public int GetHopsToBounds(int tileId) { return tileInfos[tileId].hopsToBounds; } [IteratorStateMachine(typeof(d__21))] public IEnumerable GetNeighbors(int tileId) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__21(-2) { <>4__this = this, <>3__tileId = tileId }; } [IteratorStateMachine(typeof(d__22))] public IEnumerable GetNeighbors(int tileId, int hops, Predicate predicate = null) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__22(-2) { <>4__this = this, <>3__tileId = tileId, <>3__hops = hops, <>3__predicate = predicate }; } private double ComputeTotalArea() { double num = 0.0; TileInfo[] array = tileInfos; for (int i = 0; i < array.Length; i++) { TileInfo tileInfo = array[i]; num += (double)tileInfo.area; } return num; } public MutableTileData CreateData() { return new MutableTileData(this); } public MutableTileData CreateData(T init) { return new MutableTileData(this, init); } public MutableTileData CreateData(Func init) { return new MutableTileData(this, init); } public Tiling CreateSubTiling(Predicate predicate, out SubtilingMapping mapping) { List list = new List(); Dictionary dictionary = new Dictionary(); foreach (Tile tile in Tiles) { if (predicate(tile)) { dictionary[tile.Id] = list.Count; list.Add(tile.Id); } } int[] array = new int[IsSubTiling ? subTilingMapping.Length : Count]; for (int i = 0; i < array.Length; i++) { int key = (IsSubTiling ? subTilingMapping[i] : i); array[i] = dictionary.GetValueOrDefault(key, -1); } Queue queue = new Queue(); TileInfo[] array2 = new TileInfo[list.Count]; for (int j = 0; j < list.Count; j++) { int num = list[j]; List list2 = new List(); bool flag = tileInfos[num].hopsToBounds == 0; int[] adjacency = tileInfos[num].adjacency; foreach (int key2 in adjacency) { if (dictionary.TryGetValue(key2, out var value)) { list2.Add(value); } else { flag = true; } } array2[j].center = tileInfos[num].center; array2[j].area = tileInfos[num].area; array2[j].adjacency = list2.ToArray(); array2[j].hopsToBounds = ((!flag) ? int.MaxValue : 0); if (flag) { queue.Enqueue(j); } } while (queue.Count > 0) { int num2 = queue.Dequeue(); int num3 = array2[num2].hopsToBounds + 1; int[] adjacency = array2[num2].adjacency; foreach (int num4 in adjacency) { if (array2[num4].hopsToBounds > num3) { array2[num4].hopsToBounds = num3; queue.Enqueue(num4); } } } Tiling tiling = new Tiling(Spacing, array2, spatialIndex, array); mapping = new SubtilingMapping(this, tiling, list.ToArray(), dictionary); return tiling; } } public static class TilingUtility { [CompilerGenerated] private sealed class d__2 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private Tile <>2__current; private int <>l__initialThreadId; private Tile from; public Tile <>3__from; private double radius; public double <>3__radius; private bool includeSelf; public bool <>3__includeSelf; private TileSet 5__2; private TileQueue 5__3; private double 5__4; private IEnumerator <>7__wrap4; private Tile 5__6; Tile IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__2(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 2) { try { } finally { <>m__Finally1(); } } 5__2 = null; 5__3 = null; <>7__wrap4 = null; 5__6 = default(Tile); <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = TileSet.Of(from); 5__3 = TileQueue.Of(from); 5__4 = radius * radius; if (includeSelf) { <>2__current = from; <>1__state = 1; return true; } goto IL_017a; case 1: <>1__state = -1; goto IL_017a; case 2: { <>1__state = -3; 5__3.Enqueue(5__6); 5__6 = default(Tile); goto IL_015d; } IL_017a: if (5__3.Count > 0) { <>7__wrap4 = 5__3.Dequeue().GetNeighbors().GetEnumerator(); <>1__state = -3; goto IL_015d; } return false; IL_015d: while (<>7__wrap4.MoveNext()) { 5__6 = <>7__wrap4.Current; if (5__2.Add(5__6) && !((5__6.Center - from.Center).SqrLength > 5__4)) { <>2__current = 5__6; <>1__state = 2; return true; } } <>m__Finally1(); <>7__wrap4 = null; goto IL_017a; } } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap4 != null) { <>7__wrap4.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__2 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__2(0); } d__.from = <>3__from; d__.radius = <>3__radius; d__.includeSelf = <>3__includeSelf; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } public const int UNMARKED = -1; public static Direction DirectionTo(this Tile tile, Tile to) { return new Direction(to.Center - tile.Center); } public static Tile GetNeighbor(this Tile tile, Direction direction) { double num = double.MaxValue; Tile result = default(Tile); foreach (Tile neighbor in tile.GetNeighbors()) { double num2 = Rmath.Abs(new Direction(neighbor.Center - tile.Center) - direction); if (num2 < num) { num = num2; result = neighbor; } } return result; } [IteratorStateMachine(typeof(d__2))] public static IEnumerable Scan(this Tile from, double radius, bool includeSelf = false) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__2(-2) { <>3__from = from, <>3__radius = radius, <>3__includeSelf = includeSelf }; } public static double Smooth(this Tile tile, TileData data, double range, double sigmas = 3.0, Predicate predicate = null) { double num = data[tile]; double num2 = 1.0; foreach (Tile item in tile.Scan(range)) { if (predicate == null || predicate(item)) { double num3 = Functions.Gauss2(((item.Center - tile.Center) / range).SqrLength, sigmas); num += (double)data[item] * num3; num2 += num3; } } return num / num2; } public static TileData Smooth(Tiling tiling, TileData data, double range, double sigmas = 3.0, Predicate predicate = null) { MutableTileData mutableTileData = tiling.CreateData(); foreach (Tile tile in tiling.Tiles) { if (predicate == null || predicate(tile)) { mutableTileData[tile] = tile.Smooth(data, range, sigmas, predicate); } } return mutableTileData; } public static TileData DistanceTransform(this Tiling tiling, TileData indicator, Predicate isReachable = null) { return tiling.DistanceTransform((Tile tile) => (!tile.Data(indicator)) ? null : new double?(0.0), isReachable); } public static TileData DistanceTransform(this Tiling tiling, Func indicator, Predicate isReachable = null) { return tiling.DistanceTransform((Tile tile) => (!indicator(tile)) ? null : new double?(0.0), isReachable); } public static TileData DistanceTransform(this Tiling tiling, Func init, Predicate isReachable = null) { if (isReachable == null) { isReachable = (Tile _) => true; } MutableTileData mutableTileData = tiling.CreateData((Func)((Tile tile) => (isReachable(tile) ? init(tile) : null).GetValueOrDefault(double.MaxValue))); TileQueue tileQueue = new TileQueue(tiling); foreach (Tile tile3 in tiling.Tiles) { if (isReachable(tile3) && (double)tile3.Data(mutableTileData) < double.MaxValue) { tileQueue.Enqueue(tile3); } } while (tileQueue.Count > 0) { Tile tile2 = tileQueue.Dequeue(); foreach (Tile neighbor in tile2.GetNeighbors()) { if (isReachable(neighbor)) { double num = Rfloat2.Distance(tile2.Center, neighbor.Center); if ((double)neighbor.Data(mutableTileData) > (double)tile2.Data(mutableTileData) + num) { neighbor.Data(mutableTileData) = (double)tile2.Data(mutableTileData) + num; tileQueue.Enqueue(neighbor); } } } } return mutableTileData; } public static TileData DistanceTransform(this Tiling tiling, Func init, Predicate isReachable, Func distanceFunc) { MutableTileData mutableTileData = tiling.CreateData((InertFloat)double.MaxValue); TileHeap tileHeap = new TileHeap(tiling, HeapType.Min); TileSet tileSet = new TileSet(tiling); foreach (Tile tile2 in tiling.Tiles) { if (isReachable(tile2)) { double? num = init(tile2); if (num.HasValue) { double valueOrDefault = num.GetValueOrDefault(); tileHeap.AddOrUpdate(tile2, valueOrDefault); } } } while (tileHeap.Count > 0) { double weight; Tile tile = tileHeap.Pop(out weight); mutableTileData[tile] = weight; tileSet.Add(tile); foreach (Tile neighbor in tile.GetNeighbors()) { if (isReachable(neighbor) && !tileSet.Contains(neighbor)) { double num2 = distanceFunc(tile, neighbor); if (!tileHeap.Contains(neighbor, out var weight2) || weight2 > weight + num2) { tileHeap.AddOrUpdate(neighbor, weight + num2); } } } } return mutableTileData; } public static TileList FindPathToTile(Tile origin, Tile target, Predicate isTraversable) { TileHeap tileHeap = TileHeap.InitMin(origin, 0.0); TileSet tileSet = new TileSet(origin.Tiling); Dictionary dictionary = new Dictionary(); while (tileHeap.Count > 0) { double weight; Tile tile = tileHeap.Pop(out weight); if (!tileSet.Add(tile)) { continue; } if (tile == target) { break; } foreach (Tile neighbor in tile.GetNeighbors()) { if (isTraversable(neighbor) && !tileSet.Contains(neighbor)) { double num = weight + Rfloat2.Distance(tile.Center, neighbor.Center); if (!tileHeap.Contains(neighbor, out var weight2) || weight2 > num) { dictionary[neighbor] = tile; tileHeap.AddOrUpdate(neighbor, num); } } } } return UnwindPath(origin, target, dictionary); } public static TileList FindPathToValue(Tile origin, double target, TileData values, Predicate isTraversable) { TileHeap tileHeap = TileHeap.InitMax(origin, 0.0); TileSet tileSet = new TileSet(origin.Tiling); Dictionary dictionary = new Dictionary(); while (tileHeap.Count > 0) { Tile tile = tileHeap.Pop(); if (!tileSet.Add(tile)) { continue; } if ((double)values[tile] >= target) { return UnwindPath(origin, tile, dictionary); } foreach (Tile neighbor in tile.GetNeighbors()) { if (isTraversable(neighbor) && !tileSet.Contains(neighbor) && !tileHeap.Contains(neighbor)) { dictionary[neighbor] = tile; tileHeap.AddOrUpdate(neighbor, values[neighbor]); } } } return null; } public static TileList JoinPaths(TileList lhs, TileList rhs) { if (lhs.Count == 0 || rhs.Count == 0) { throw new ArgumentException("empty path"); } if (lhs[lhs.Count - 1] != rhs[0]) { throw new ArgumentException("paths don't meet"); } TileList tileList = new TileList(lhs); foreach (Tile item in rhs.Skip(1)) { tileList.Add(item); } return tileList; } private static TileList UnwindPath(Tile origin, Tile target, Dictionary backlinks) { TileList tileList = new TileList(origin.Tiling); Tile value = target; while (value != origin) { tileList.Add(value); if (!backlinks.TryGetValue(value, out value)) { return null; } } tileList.Add(origin); tileList.Reverse(); return tileList; } public static MutableTileData MarkConnectedComponents(this Tiling tiling, Predicate predicate) { MutableTileData mutableTileData = tiling.CreateData(-1); int num = 0; TileQueue tileQueue = new TileQueue(tiling); foreach (Tile tile in tiling.Tiles) { if (mutableTileData[tile] != -1 || !predicate(tile)) { continue; } mutableTileData[tile] = num; tileQueue.Enqueue(tile); while (tileQueue.Count > 0) { foreach (Tile neighbor in tileQueue.Dequeue().GetNeighbors()) { if (predicate(neighbor) && mutableTileData[neighbor] == -1) { mutableTileData[neighbor] = num; tileQueue.Enqueue(neighbor); } } } num++; } return mutableTileData; } public static TileList FindLocalMaxima(this Tiling tiling, TileData data, double spacing = 0.0) { TileList tileList = new TileList(tiling); foreach (Tile tile in tiling.Tiles) { if (tile.GetNeighbors().All((Tile neighbor) => (double)data[neighbor] < (double)data[tile])) { tileList.Add(tile); } } tileList.Sort((Tile a, Tile b) => -((double)data[a]).CompareTo(data[b])); if (spacing <= 0.0) { return tileList; } TileSet selection = new TileSet(tiling); foreach (Tile item in tileList) { if (!item.Scan(spacing).Any((Tile t) => selection.Contains(t))) { selection.Add(item); } } TileList tileList2 = new TileList(tiling); foreach (Tile item2 in tileList) { if (selection.Contains(item2)) { tileList2.Add(item2); } } return tileList2; } public static TileData BuildHopMap(this Tiling tiling, int maxHops) { return tiling.CreateData((Tile tile) => (from t in tile.GetNeighbors(maxHops) select t.Id).ToArray()); } public static TileData BuildScanMap(this Tiling tiling, double scanRange) { return tiling.CreateData((Tile tile) => (from t in tile.Scan(scanRange) select t.Id).ToArray()); } } public class VoronoiTilingBuilder { private readonly DelaunayGraph graph; private readonly double spacingHint; private readonly double boundingRadius; public VoronoiTilingBuilder(InertFloat2[] points, double spacingHint, double boundingRadius) { graph = new DelaunayGraph(points); this.spacingHint = spacingHint; this.boundingRadius = boundingRadius; } private static Tiling.TileInfo[] BuildTileInfos(DelaunayGraph graph) { Tiling.TileInfo[] array = new Tiling.TileInfo[graph.PrunedCount]; Rfloat2[] array2 = graph.Points.ToArray(); double[] voronoiAreas = graph.VoronoiAreas; for (int i = 0; i < graph.PrunedCount; i++) { array[i] = new Tiling.TileInfo { center = array2[i], area = voronoiAreas[i], adjacency = graph.GetAdjacency(i), hopsToBounds = graph.GetHopsToBounds(i) }; } return array; } public Tiling Build() { return new Tiling(spacingHint, BuildTileInfos(graph), new VoronoiSpatialIndex(graph, spacingHint, boundingRadius)); } } public class VoronoiSpatialIndex : ITilingSpatialIndex { private struct TilePointRef { public InertFloat2 pos; public int index; } private readonly List[,] grid; private readonly double queryRadius; private readonly Rfloat2 offset; private readonly double cellSize; private readonly double? boundingR2; private readonly BoundingBox boundingBox; public VoronoiSpatialIndex(DelaunayGraph graph, double spacingHint, double? boundingRadius) { BoundingBox bounds = graph.Bounds; Rint2 rint = (bounds.Size / spacingHint).CeilToInt() + Rint2.One; grid = new List[rint.x, rint.y]; offset = bounds.Center - 0.5 * spacingHint * (Rfloat2)rint; cellSize = spacingHint; FillGrid(graph); boundingR2 = (boundingRadius.HasValue ? new double?(boundingRadius.Value * boundingRadius.Value) : null); boundingBox = bounds; queryRadius = spacingHint * 0.5; } private Rint2 GridPos(Rfloat2 pos) { return ((pos - offset) / cellSize).RoundToInt(); } private bool InBounds(Rfloat2 pos) { if (!boundingR2.HasValue) { return boundingBox.Contains(pos); } return pos.SqrLength <= boundingR2.Value; } private void FillGrid(DelaunayGraph graph) { for (int i = 0; i < grid.GetLength(0); i++) { for (int j = 0; j < grid.GetLength(1); j++) { grid[i, j] = new List(); } } Rfloat2[] array = graph.Points.ToArray(); for (int k = 0; k < array.Length; k++) { Rfloat2 rfloat = array[k]; Rint2 rint = GridPos(rfloat); grid[rint.x, rint.y].Add(new TilePointRef { pos = rfloat, index = ((k < graph.PrunedCount) ? k : (-1)) }); } } public int GetTileId(Rfloat2 pos) { if (!InBounds(pos)) { return -1; } double num = queryRadius; do { double num2 = num * num; double num3 = double.MaxValue; int result = -1; Rint2 rint = GridPos(pos - Rfloat2.One * num); Rint2 rint2 = GridPos(pos + Rfloat2.One * num); for (int i = Rmath.Max(0, rint.x); i <= Rmath.Min(grid.GetLength(0) - 1, rint2.x); i++) { for (int j = Rmath.Max(0, rint.y); j <= Rmath.Min(grid.GetLength(1) - 1, rint2.y); j++) { foreach (TilePointRef item in grid[i, j]) { double sqrLength = (pos - item.pos).SqrLength; if (sqrLength < num2 && sqrLength < num3) { num3 = sqrLength; result = item.index; } } } } if (num3 < double.MaxValue) { return result; } num *= 1.4; } while (num < 100.0 * queryRadius); return -1; } } } namespace Riverheim.Util.Tilings.Processing { public class DropletErosionSimulator { [Serializable] public struct Config { public double bedrockLevel; public double evaporationRate; public double sedimentCapacity; public double erosionRate; public double slopePower; } private class Droplet { public double water; public double speed; public double sediment; } private readonly Config config; private readonly Predicate isDrain; private readonly Predicate isReachable; private const int MAX_ITERATIONS = 100; public DropletErosionSimulator(Config config, Predicate isReachable, Predicate isDrain) { this.config = config; this.isDrain = isDrain; this.isReachable = isReachable; } public void Run(RandomSequence random, Tiling tiling, MutableTileData heights, int maxIterations) { TileList tileList = new TileList(tiling); foreach (Tile tile2 in tiling.Tiles) { if (isReachable(tile2)) { tileList.Add(tile2); } } if (tileList.Count != 0) { for (int i = 0; i < maxIterations; i++) { Tile tile = tileList[random.Single.Range(tileList.Count - 1)]; RunAt(heights, tile); } } } public TileList RunAt(MutableTileData heights, Tile tile) { Droplet droplet = new Droplet { water = 1.0 }; TileList tileList = new TileList(tile.Tiling); Tile tile2 = tile; int num = 0; while (num++ < 100 && !isDrain(tile)) { tileList.Add(tile); Tile tile3 = FindMostSlopedNeighbor(heights, tile); if ((double)heights[tile3] >= (double)heights[tile]) { ref InertFloat reference = ref heights[tile]; reference = (double)reference + droplet.sediment; break; } double value = ((double)heights[tile] - (double)heights[tile3]) / (tile3.Center - tile.Center).Length; droplet.speed = Rmath.Pow(value, config.slopePower); droplet.water *= 1.0 - config.evaporationRate; double num2 = config.sedimentCapacity * droplet.water * droplet.speed; if (droplet.sediment > num2) { ref InertFloat reference2 = ref heights[tile]; reference2 = (double)reference2 + Rmath.Min(droplet.sediment - num2, (double)tile2.Data(heights) - (double)tile.Data(heights)); droplet.sediment = num2; } double b = Rmath.Max(0.0, (double)heights[tile] - config.bedrockLevel); double a = num2 * config.erosionRate; a = Rmath.Min(a, b); droplet.sediment += a; ref InertFloat reference3 = ref heights[tile]; reference3 = (double)reference3 - a; tile2 = tile; tile = tile3; } if (num >= 100) { Logger.LogWarning($"Max droplet simulation iterations reached for tile {tile}"); } return tileList; } private Tile FindMostSlopedNeighbor(TileData heights, Tile tile) { double num = double.MaxValue; Tile result = default(Tile); foreach (Tile neighbor in tile.GetNeighbors()) { if (isReachable(neighbor)) { double num2 = (double)heights[neighbor] - (double)heights[tile]; double num3 = num2 * Rmath.Abs(num2) / (neighbor.Center - tile.Center).SqrLength; if (num > num3) { num = num3; result = neighbor; } } } return result; } } public class ImpactErosionSimulator { [Serializable] public struct Config { public double rate; public double reach; } private readonly Config config; private readonly Predicate isReachable; public ImpactErosionSimulator(Config config, Predicate isReachable) { this.config = config; this.isReachable = isReachable; } public TileList RunAt(MutableTileData heights, Tile origin) { double h = (double)heights[origin] - config.rate; TileList tileList = new TileList(origin.Tiling); foreach (Tile item in origin.Scan(config.reach, includeSelf: true)) { if (isReachable(item)) { heights[item] = CalcHeight(heights[item], h, (item.Center - origin.Center).SqrLength); tileList.Add(item); } } return tileList; } private double CalcHeight(double h, double h0, double d2) { double num = Rmath.Smooth3(Rmath.Clamp01((h - h0) / config.rate)); return Rmath.Lerp((1.0 - d2 / (config.reach * config.reach)) * num, h, h - config.rate); } } } namespace Riverheim.Util.Tilings.Internal { public class DelaunayGraph { [CompilerGenerated] private sealed class d__8 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private int <>2__current; private int <>l__initialThreadId; private Dictionary hopsToBounds; public Dictionary <>3__hopsToBounds; private Dictionary> graph; public Dictionary> <>3__graph; private HashSet.Enumerator <>7__wrap1; int IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__8(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = default(HashSet.Enumerator); <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: { <>1__state = -1; int num = 0; int item = -1; foreach (KeyValuePair hopsToBound in hopsToBounds) { if (hopsToBound.Value > num) { num = hopsToBound.Value; item = hopsToBound.Key; } } HashSet hashSet = new HashSet(graph.Keys); hashSet.Remove(item); Queue queue = new Queue(); queue.Enqueue(item); while (queue.Count > 0) { int key = queue.Dequeue(); foreach (int item2 in graph[key]) { if (hashSet.Contains(item2)) { hashSet.Remove(item2); queue.Enqueue(item2); } } } <>7__wrap1 = hashSet.GetEnumerator(); <>1__state = -3; break; } case 1: <>1__state = -3; break; } if (<>7__wrap1.MoveNext()) { int current3 = <>7__wrap1.Current; <>2__current = current3; <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap1 = default(HashSet.Enumerator); return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; ((IDisposable)<>7__wrap1).Dispose(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__8 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__8(0); } d__.graph = <>3__graph; d__.hopsToBounds = <>3__hopsToBounds; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } private readonly Delaunator delaunay; private readonly Dictionary> adjacency; private readonly Dictionary boundsHops; private readonly int[] prunedToOriginal; private readonly int[] originalToPruned; public int PrunedCount => adjacency.Count; public IEnumerable Points => prunedToOriginal.Select((int oldIdx) => delaunay.Points[oldIdx].ToRfloat2()); public IEnumerable HullPoints => delaunay.Hull.Select((int oldIdx) => delaunay.Points[oldIdx].ToRfloat2()); public IEnumerable Triangles => delaunay.Triangles.Select((int oldIdx) => originalToPruned[oldIdx]); public double[] VoronoiAreas => ComputeAreas(delaunay.GetVoronoiCellsBasedOnCircumcenters()); public double[] BarycentricDualAreas => ComputeAreas(delaunay.GetVoronoiCellsBasedOnCentroids()); public BoundingBox Bounds => new BoundingBox(HullPoints); public DelaunayGraph(InertFloat2[] points) { delaunay = ComputeDelaunay(points); adjacency = delaunay.ComputeAdjacency(); boundsHops = ComputeHopsToBounds(adjacency, new HashSet(delaunay.Hull), -2); foreach (int key in boundsHops.Keys) { if (boundsHops[key] >= 0) { continue; } foreach (int item in adjacency[key]) { if (adjacency.ContainsKey(item)) { adjacency[item].Remove(key); } } adjacency.Remove(key); } foreach (int item2 in FindDisconnectedVertices(adjacency, boundsHops)) { adjacency.Remove(item2); } if (adjacency.Count == 0) { throw new Exception("DelaunayGraph left with no vertices after culling border!"); } prunedToOriginal = ComputeReordering(); originalToPruned = new int[prunedToOriginal.Length]; for (int i = 0; i < prunedToOriginal.Length; i++) { originalToPruned[prunedToOriginal[i]] = i; } } private static Delaunator ComputeDelaunay(InertFloat2[] points) { IPoint[] array = new IPoint[points.Length]; for (int i = 0; i < array.Length; i++) { Rfloat2 rfloat = points[i]; array[i] = new Point(rfloat.x, rfloat.y); } return new Delaunator(array); } private static Dictionary ComputeHopsToBounds(Dictionary> graph, HashSet bounds, int start = 0) { Dictionary dictionary = new Dictionary(); foreach (int key2 in graph.Keys) { dictionary[key2] = (bounds.Contains(key2) ? start : int.MaxValue); } Queue queue = new Queue(bounds); while (queue.Count > 0) { int key = queue.Dequeue(); int num = dictionary[key] + 1; foreach (int item in graph[key]) { if (dictionary[item] > num) { dictionary[item] = num; queue.Enqueue(item); } } } return dictionary; } [IteratorStateMachine(typeof(d__8))] private static IEnumerable FindDisconnectedVertices(Dictionary> graph, Dictionary hopsToBounds) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__8(-2) { <>3__graph = graph, <>3__hopsToBounds = hopsToBounds }; } private int[] ComputeReordering() { int[] array = Enumerable.Range(0, delaunay.Points.Length).ToArray(); int num = delaunay.Points.Length; for (int i = 0; i < num; i++) { while (!adjacency.ContainsKey(array[i]) && i < num) { ref int reference = ref array[i]; ref int reference2 = ref array[num - 1]; int num2 = array[num - 1]; int num3 = array[i]; reference = num2; reference2 = num3; num--; } } return array; } public int[] GetAdjacency(int idx) { return adjacency[prunedToOriginal[idx]].Select((int i) => originalToPruned[i]).ToArray(); } public int GetHopsToBounds(int idx) { return boundsHops[prunedToOriginal[idx]]; } private double[] ComputeAreas(IEnumerable<IVoronoiCell> cells) { double[] array = new double[PrunedCount]; foreach (IVoronoiCell cell in cells) { int num = originalToPruned[cell.Index]; if (num < PrunedCount) { array[num] = cell.Area(); } } return array; } } internal static class DelaunayExtensions { [CompilerGenerated] private sealed class d__0 : IEnumerable<(int, int)>, IEnumerable, IEnumerator<(int, int)>, IEnumerator, IDisposable { private int <>1__state; private (int, int) <>2__current; private int <>l__initialThreadId; private Delaunator delaunay; public Delaunator <>3__delaunay; private int 5__2; private List 5__3; private int 5__4; (int, int) IEnumerator<(int, int)>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { 5__3 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; int num2 = 5__4 + 1; 5__4 = num2; goto IL_00b4; } <>1__state = -1; 5__2 = 0; goto IL_00de; IL_00b4: if (5__4 < 5__3.Count) { int index = ((5__4 < 5__3.Count - 1) ? (5__4 + 1) : 0); <>2__current = (5__3[5__4], 5__3[index]); <>1__state = 1; return true; } 5__3 = null; 5__2++; goto IL_00de; IL_00de: if (5__2 < delaunay.Triangles.Length / 3) { 5__3 = new List(delaunay.PointsOfTriangle(5__2)); 5__4 = 0; goto IL_00b4; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<(int, int)> IEnumerable<(int, int)>.GetEnumerator() { d__0 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__0(0); } d__.delaunay = <>3__delaunay; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<(int, int)>)this).GetEnumerator(); } } [IteratorStateMachine(typeof(d__0))] private static IEnumerable<(int, int)> GetEdgeConnections(this Delaunator delaunay) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__0(-2) { <>3__delaunay = delaunay }; } public static Dictionary> ComputeAdjacency(this Delaunator delaunay) { Dictionary> dictionary = new Dictionary>(); foreach (var (key, item) in delaunay.GetEdgeConnections()) { if (!dictionary.ContainsKey(key)) { dictionary[key] = new List(); } dictionary[key].Add(item); } return dictionary; } public static Rfloat2 ToRfloat2(this IPoint p) { return new Rfloat2(p.X, p.Y); } public static double Area(this IVoronoiCell cell) { double num = 0.0; for (int i = 0; i < cell.Points.Length; i++) { num += Rfloat2.Cross(cell.Points[i].ToRfloat2(), cell.Points[(i + 1) % cell.Points.Length].ToRfloat2()); } return Rmath.Max(0.0, num * 0.5); } } } namespace Riverheim.Util.Tilings.Containers { public class RandomTileHeap { private readonly TileHeap heap; private readonly RandomIndexer indexer; private readonly double temperature; public int Count => heap.Count; public RandomTileHeap(Tiling tiling, RandomIndexer indexer, double temperature = 1.0) { heap = new TileHeap(tiling, HeapType.Max); this.indexer = indexer; this.temperature = temperature; } public void AddOrUpdate(Tile tile, double loss) { heap.AddOrUpdate(tile, indexer.Single(tile.Id).Gumbel(0.0 - loss, temperature)); } public Tile Pop() { return heap.Pop(); } } public class TileHeap { private readonly Tiling tiling; private readonly IntHeap impl; public int Count => impl.Count; public TileHeap(Tiling tiling, HeapType type) { this.tiling = tiling; impl = new IntHeap(type); } public static TileHeap InitMin(Tile init, double weight) { TileHeap tileHeap = new TileHeap(init.Tiling, HeapType.Min); tileHeap.AddOrUpdate(init, weight); return tileHeap; } public static TileHeap InitMax(Tile init, double weight) { TileHeap tileHeap = new TileHeap(init.Tiling, HeapType.Max); tileHeap.AddOrUpdate(init, weight); return tileHeap; } public bool Contains(Tile tile) { return impl.Contains(tile.Id); } public bool Contains(Tile tile, out double weight) { return impl.Contains(tile.Id, out weight); } public bool AddOrUpdate(Tile tile, double weight) { return impl.AddOrUpdate(tile.Id, weight); } public bool Remove(Tile tile) { return impl.Remove(tile.Id); } public Tile Peek() { return tiling.GetTile(impl.Peek()); } public Tile Peek(out double weight) { return tiling.GetTile(impl.Peek(out weight)); } public Tile Pop() { return tiling.GetTile(impl.Pop()); } public Tile Pop(out double weight) { return tiling.GetTile(impl.Pop(out weight)); } } public class TileList : IEnumerable, IEnumerable { private readonly Tiling tiling; private readonly List impl; public int Count => impl.Count; public Tile this[int index] { get { return tiling.GetTile(impl[index]); } set { impl[index] = value.Id; } } public TileList(Tiling tiling) { this.tiling = tiling; impl = new List(); } public TileList(TileList other) { tiling = other.tiling; impl = new List(other.impl); } public static TileList Of(params Tile[] tiles) { if (tiles.Length == 0) { throw new ArgumentException("no tiles provided"); } TileList tileList = new TileList(tiles[0].Tiling); foreach (Tile tile in tiles) { tileList.Add(tile); } return tileList; } public void Add(Tile tile) { impl.Add(tile.Id); } public void Remove(Tile tile) { impl.Remove(tile.Id); } public Tile Find(Predicate predicate) { return tiling.GetTile(impl.Find((int id) => predicate(tiling.GetTile(id)))); } public void Sort(Comparison comparison) { impl.Sort((int a, int b) => comparison(tiling.GetTile(a), tiling.GetTile(b))); } public void Reverse() { impl.Reverse(); } public TileList Reversed() { TileList tileList = new TileList(this); tileList.impl.Reverse(); return tileList; } public IEnumerator GetEnumerator() { return impl.Select((int id) => tiling.GetTile(id)).GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public class TileQueue { private readonly Tiling tiling; private readonly Queue impl; public int Count => impl.Count; public TileQueue(Tiling tiling) { this.tiling = tiling; impl = new Queue(); } public static TileQueue Of(params Tile[] tiles) { if (tiles.Length == 0) { throw new ArgumentException("no tiles provided"); } TileQueue tileQueue = new TileQueue(tiles[0].Tiling); foreach (Tile tile in tiles) { tileQueue.Enqueue(tile); } return tileQueue; } public void Enqueue(Tile tile) { impl.Enqueue(tile.Id); } public Tile Dequeue() { return tiling.GetTile(impl.Dequeue()); } } public class TileSet { private readonly Tiling tiling; private readonly HashSet impl; public int Count => impl.Count; public TileSet(Tiling tiling) { this.tiling = tiling; impl = new HashSet(); } public static TileSet Of(params Tile[] tiles) { if (tiles.Length == 0) { throw new ArgumentException("no tiles provided"); } TileSet tileSet = new TileSet(tiles[0].Tiling); foreach (Tile tile in tiles) { tileSet.Add(tile); } return tileSet; } public static TileSet All(Tiling tiling) { TileSet tileSet = new TileSet(tiling); foreach (Tile tile in tiling.Tiles) { tileSet.Add(tile); } return tileSet; } public bool Contains(Tile tile) { return impl.Contains(tile.Id); } public bool Add(Tile tile) { return impl.Add(tile.Id); } public void Remove(Tile tile) { impl.Remove(tile.Id); } public void UnionWith(TileSet other) { impl.UnionWith(other.impl); } public IEnumerable ConsistentOrdering() { int[] array = impl.ToArray(); Array.Sort(array); return array.Select(tiling.GetTile); } public IEnumerable Unordered() { return impl.Select(tiling.GetTile); } } public class TileStack { private readonly Tiling tiling; private readonly Stack impl; public int Count => impl.Count; public TileStack(Tiling tiling) { this.tiling = tiling; impl = new Stack(); } public static TileStack Of(params Tile[] tiles) { if (tiles.Length == 0) { throw new ArgumentException("no tiles provided"); } TileStack tileStack = new TileStack(tiles[0].Tiling); foreach (Tile tile in tiles) { tileStack.Push(tile); } return tileStack; } public void Push(Tile tile) { impl.Push(tile.Id); } public Tile Pop() { return tiling.GetTile(impl.Pop()); } public Tile Peek() { return tiling.GetTile(impl.Peek()); } } } namespace Riverheim.Util.Splines { public class BSplineImpl : ISplineImpl { private readonly Rfloat2[] points; public int SegmentCount => points.Length - 3; public BSplineImpl(ICollection points) { if (points.Count < 2) { throw new ArgumentException("Spline must have at least 2 points"); } this.points = new Rfloat2[points.Count + 2]; int num = 1; foreach (Rfloat2 point in points) { this.points[num++] = point; } this.points[0] = 2.0 * this.points[1] - this.points[2]; this.points[num] = 2.0 * this.points[num - 1] - this.points[num - 2]; } public Rfloat2 EvaluatePosition(in PolylineProgress pos) { double segmentProgress; int num = pos.SegmentIndex(SegmentCount, out segmentProgress); double num2 = segmentProgress * segmentProgress; double num3 = segmentProgress * segmentProgress * segmentProgress; Rfloat2 rfloat = points[num]; Rfloat2 rfloat2 = points[num + 1]; Rfloat2 rfloat3 = points[num + 2]; Rfloat2 rfloat4 = points[num + 3]; return 1.0 / 6.0 * (rfloat * (-1.0 * num3 + 3.0 * num2 - 3.0 * segmentProgress + 1.0) + rfloat2 * (3.0 * num3 - 6.0 * num2 + 4.0) + rfloat3 * (-3.0 * num3 + 3.0 * num2 + 3.0 * segmentProgress + 1.0) + rfloat4 * num3); } public Rfloat2 EvaluateTangent(in PolylineProgress pos) { double segmentProgress; int num = pos.SegmentIndex(SegmentCount, out segmentProgress); double num2 = segmentProgress * segmentProgress; Rfloat2 rfloat = points[num]; Rfloat2 rfloat2 = points[num + 1]; Rfloat2 rfloat3 = points[num + 2]; Rfloat2 rfloat4 = points[num + 3]; return 1.0 / 6.0 * (rfloat * (-3.0 * num2 + 6.0 * segmentProgress - 3.0) + rfloat2 * (9.0 * num2 - 12.0 * segmentProgress) + rfloat3 * (-9.0 * num2 + 6.0 * segmentProgress + 3.0) + rfloat4 * 3.0 * num2); } public Rfloat2 EvaluateAcceleration(in PolylineProgress pos) { double segmentProgress; int num = pos.SegmentIndex(SegmentCount, out segmentProgress); Rfloat2 rfloat = points[num]; Rfloat2 rfloat2 = points[num + 1]; Rfloat2 rfloat3 = points[num + 2]; Rfloat2 rfloat4 = points[num + 3]; return 1.0 / 6.0 * (rfloat * (-6.0 * segmentProgress + 6.0) + rfloat2 * (18.0 * segmentProgress - 12.0) + rfloat3 * (-18.0 * segmentProgress + 6.0) + rfloat4 * 6.0 * segmentProgress); } Rfloat2 ISplineImpl.EvaluatePosition(in PolylineProgress pos) { return EvaluatePosition(in pos); } Rfloat2 ISplineImpl.EvaluateTangent(in PolylineProgress pos) { return EvaluateTangent(in pos); } Rfloat2 ISplineImpl.EvaluateAcceleration(in PolylineProgress pos) { return EvaluateAcceleration(in pos); } } public class HermiteSplineImpl : ISplineImpl { private readonly Rfloat2[] points; private readonly Rfloat2[] tangents; public int SegmentCount => points.Length - 1; public HermiteSplineImpl(ICollection points, ICollection tangents) { if (points.Count < 2) { throw new ArgumentException("Spline must have at least 2 points"); } if (points.Count != tangents.Count) { throw new ArgumentException("Number of points and tangents must be the same"); } this.points = points.ToArray(); this.tangents = tangents.ToArray(); } public Rfloat2 EvaluatePosition(in PolylineProgress pos) { double segmentProgress; int num = pos.SegmentIndex(SegmentCount, out segmentProgress); double num2 = segmentProgress * segmentProgress; double num3 = segmentProgress * segmentProgress * segmentProgress; Rfloat2 rfloat = points[num]; Rfloat2 rfloat2 = points[num + 1]; Rfloat2 rfloat3 = tangents[num]; Rfloat2 rfloat4 = tangents[num + 1]; return (2.0 * num3 - 3.0 * num2 + 1.0) * rfloat + (num3 - 2.0 * num2 + segmentProgress) * rfloat3 + (-2.0 * num3 + 3.0 * num2) * rfloat2 + (num3 - num2) * rfloat4; } public Rfloat2 EvaluateTangent(in PolylineProgress pos) { double segmentProgress; int num = pos.SegmentIndex(SegmentCount, out segmentProgress); double num2 = segmentProgress * segmentProgress; Rfloat2 rfloat = points[num]; Rfloat2 rfloat2 = points[num + 1]; Rfloat2 rfloat3 = tangents[num]; Rfloat2 rfloat4 = tangents[num + 1]; return (6.0 * num2 - 6.0 * segmentProgress) * rfloat + (3.0 * num2 - 4.0 * segmentProgress + 1.0) * rfloat3 + (-6.0 * num2 + 6.0 * segmentProgress) * rfloat2 + (3.0 * num2 - 2.0 * segmentProgress) * rfloat4; } public Rfloat2 EvaluateAcceleration(in PolylineProgress pos) { double segmentProgress; int num = pos.SegmentIndex(SegmentCount, out segmentProgress); Rfloat2 rfloat = points[num]; Rfloat2 rfloat2 = points[num + 1]; Rfloat2 rfloat3 = tangents[num]; Rfloat2 rfloat4 = tangents[num + 1]; return (12.0 * segmentProgress - 6.0) * rfloat + (6.0 * segmentProgress - 4.0) * rfloat3 + (-12.0 * segmentProgress + 6.0) * rfloat2 + (6.0 * segmentProgress - 2.0) * rfloat4; } Rfloat2 ISplineImpl.EvaluatePosition(in PolylineProgress pos) { return EvaluatePosition(in pos); } Rfloat2 ISplineImpl.EvaluateTangent(in PolylineProgress pos) { return EvaluateTangent(in pos); } Rfloat2 ISplineImpl.EvaluateAcceleration(in PolylineProgress pos) { return EvaluateAcceleration(in pos); } } public interface ISplineImpl { int SegmentCount { get; } Rfloat2 EvaluatePosition(in PolylineProgress pos); Rfloat2 EvaluateTangent(in PolylineProgress pos); Rfloat2 EvaluateAcceleration(in PolylineProgress pos); } public class PolylineData { public delegate T Interpolator(double t, T lhs, T rhs); private readonly T[] data; private readonly Interpolator interpolator; public int Count => data.Length; public T this[int index] => data[index]; public PolylineData(ICollection data, Interpolator interpolator) { this.data = data.ToArray(); this.interpolator = interpolator; } public T Evaluate(PolylineProgress progress) { double segmentProgress; int num = progress.SegmentIndex(data.Length - 1, out segmentProgress); return interpolator(segmentProgress, data[num], data[num + 1]); } public PolylineData Transform(Func transform, PolylineData.Interpolator interpolator) { return new PolylineData(data.Select(transform).ToArray(), interpolator); } } public static class PolylineDataExtension { [CompilerGenerated] private sealed class d__1 : IEnumerable<(Segment, T, T)>, IEnumerable, IEnumerator<(Segment, T, T)>, IEnumerator, IDisposable { private int <>1__state; private (Segment, T, T) <>2__current; private int <>l__initialThreadId; private Polyline polyline; public Polyline <>3__polyline; private PolylineData data; public PolylineData <>3__data; private int 5__2; private IEnumerator <>7__wrap2; (Segment, T, T) IEnumerator<(Segment, T, T)>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__1(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap2 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = 0; <>7__wrap2 = polyline.Segments.GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; 5__2++; break; } if (<>7__wrap2.MoveNext()) { Segment current = <>7__wrap2.Current; <>2__current = (current, data[5__2], data[5__2 + 1]); <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap2 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap2 != null) { <>7__wrap2.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<(Segment, T, T)> IEnumerable<(Segment, T, T)>.GetEnumerator() { d__1 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__1(0); } d__.polyline = <>3__polyline; d__.data = <>3__data; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<(Segment, T, T)>)this).GetEnumerator(); } } [CompilerGenerated] private sealed class d__0 : IEnumerable<(Rfloat2, T)>, IEnumerable, IEnumerator<(Rfloat2, T)>, IEnumerator, IDisposable { private int <>1__state; private (Rfloat2, T) <>2__current; private int <>l__initialThreadId; private Polyline polyline; public Polyline <>3__polyline; private PolylineData data; public PolylineData <>3__data; private int 5__2; private IEnumerator <>7__wrap2; (Rfloat2, T) IEnumerator<(Rfloat2, T)>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__0(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap2 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: <>1__state = -1; 5__2 = 0; <>7__wrap2 = polyline.Vertices.GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; break; } if (<>7__wrap2.MoveNext()) { Rfloat2 current = <>7__wrap2.Current; <>2__current = (current, data[5__2++]); <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap2 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap2 != null) { <>7__wrap2.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator<(Rfloat2, T)> IEnumerable<(Rfloat2, T)>.GetEnumerator() { d__0 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__0(0); } d__.polyline = <>3__polyline; d__.data = <>3__data; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable<(Rfloat2, T)>)this).GetEnumerator(); } } [IteratorStateMachine(typeof(d__0<>))] public static IEnumerable<(Rfloat2, T)> Vertices(this Polyline polyline, PolylineData data) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__0(-2) { <>3__polyline = polyline, <>3__data = data }; } [IteratorStateMachine(typeof(d__1<>))] public static IEnumerable<(Segment, T, T)> Segments(this Polyline polyline, PolylineData data) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__1(-2) { <>3__polyline = polyline, <>3__data = data }; } } public readonly struct PolylineProgress { private readonly double progress; private PolylineProgress(double progress) { this.progress = Rmath.Clamp01(progress); } public PolylineProgress(int segmentCount, int segmentIndex, double segmentProgress) : this(((double)segmentIndex + segmentProgress) / (double)segmentCount) { } public int SegmentIndex(int segmentCount, out double segmentProgress) { double num = progress * (double)segmentCount; int num2 = Rmath.FloorToInt(num); if (num2 == segmentCount) { segmentProgress = 1.0; num2 = segmentCount - 1; } else { segmentProgress = Rmath.Clamp01(num - (double)num2); } return num2; } public static implicit operator PolylineProgress(double progress) { return new PolylineProgress(progress); } public static implicit operator double(PolylineProgress progress) { return progress.progress; } } public class QuinticHermiteSplineImpl : ISplineImpl { private readonly Rfloat2[] points; private readonly Rfloat2[] tangents; private readonly Rfloat2[] accelerations; public int SegmentCount => points.Length - 1; public QuinticHermiteSplineImpl(ICollection points, ICollection tangents, ICollection accelerations) { if (points.Count < 2) { throw new ArgumentException("Spline must have at least 2 points"); } if (points.Count != tangents.Count) { throw new ArgumentException("Number of points and tangents must be the same"); } if (points.Count != accelerations.Count) { throw new ArgumentException("Number of points and accelerations must be the same"); } this.points = points.ToArray(); this.tangents = tangents.ToArray(); this.accelerations = accelerations.ToArray(); } public Rfloat2 EvaluatePosition(in PolylineProgress pos) { double segmentProgress; int num = pos.SegmentIndex(SegmentCount, out segmentProgress); double num2 = segmentProgress * segmentProgress; double num3 = segmentProgress * segmentProgress * segmentProgress; double num4 = segmentProgress * segmentProgress * segmentProgress * segmentProgress; double num5 = segmentProgress * segmentProgress * segmentProgress * segmentProgress * segmentProgress; Rfloat2 rfloat = points[num]; Rfloat2 rfloat2 = points[num + 1]; Rfloat2 rfloat3 = tangents[num]; Rfloat2 rfloat4 = tangents[num + 1]; Rfloat2 rfloat5 = accelerations[num]; Rfloat2 rfloat6 = accelerations[num + 1]; return rfloat * (1.0 - 10.0 * num3 + 15.0 * num4 - 6.0 * num5) + rfloat3 * (segmentProgress - 6.0 * num3 + 8.0 * num4 - 3.0 * num5) + rfloat5 * 0.5 * (num2 - 3.0 * num3 + 3.0 * num4 - num5) + rfloat2 * (10.0 * num3 - 15.0 * num4 + 6.0 * num5) + rfloat4 * (-4.0 * num3 + 7.0 * num4 - 3.0 * num5) + rfloat6 * 0.5 * (num3 - 2.0 * num4 + num5); } public Rfloat2 EvaluateTangent(in PolylineProgress pos) { double segmentProgress; int num = pos.SegmentIndex(SegmentCount, out segmentProgress); double num2 = segmentProgress * segmentProgress; double num3 = segmentProgress * segmentProgress * segmentProgress; double num4 = segmentProgress * segmentProgress * segmentProgress * segmentProgress; Rfloat2 rfloat = points[num]; Rfloat2 rfloat2 = points[num + 1]; Rfloat2 rfloat3 = tangents[num]; Rfloat2 rfloat4 = tangents[num + 1]; Rfloat2 rfloat5 = accelerations[num]; Rfloat2 rfloat6 = accelerations[num + 1]; return rfloat * (-30.0 * num2 + 60.0 * num3 - 30.0 * num4) + rfloat3 * (1.0 - 18.0 * num2 + 32.0 * num3 - 15.0 * num4) + rfloat5 * 0.5 * (2.0 * segmentProgress - 9.0 * num2 + 12.0 * num3 - 5.0 * num4) + rfloat2 * (30.0 * num2 - 60.0 * num3 + 30.0 * num4) + rfloat4 * (-12.0 * num2 + 28.0 * num3 - 15.0 * num4) + rfloat6 * 0.5 * (3.0 * num2 - 8.0 * num3 + 5.0 * num4); } public Rfloat2 EvaluateAcceleration(in PolylineProgress pos) { double segmentProgress; int num = pos.SegmentIndex(SegmentCount, out segmentProgress); double num2 = segmentProgress * segmentProgress; double num3 = segmentProgress * segmentProgress * segmentProgress; Rfloat2 rfloat = points[num]; Rfloat2 rfloat2 = points[num + 1]; Rfloat2 rfloat3 = tangents[num]; Rfloat2 rfloat4 = tangents[num + 1]; Rfloat2 rfloat5 = accelerations[num]; Rfloat2 rfloat6 = accelerations[num + 1]; return rfloat * (-60.0 * segmentProgress + 180.0 * num2 - 120.0 * num3) + rfloat3 * (-36.0 * segmentProgress + 96.0 * num2 - 60.0 * num3) + rfloat5 * 0.5 * (2.0 - 18.0 * segmentProgress + 36.0 * num2 - 20.0 * num3) + rfloat2 * (60.0 * segmentProgress - 180.0 * num2 + 120.0 * num3) + rfloat4 * (-24.0 * segmentProgress + 84.0 * num2 - 60.0 * num3) + rfloat6 * 0.5 * (6.0 * segmentProgress - 24.0 * num2 + 20.0 * num3); } Rfloat2 ISplineImpl.EvaluatePosition(in PolylineProgress pos) { return EvaluatePosition(in pos); } Rfloat2 ISplineImpl.EvaluateTangent(in PolylineProgress pos) { return EvaluateTangent(in pos); } Rfloat2 ISplineImpl.EvaluateAcceleration(in PolylineProgress pos) { return EvaluateAcceleration(in pos); } } public class Spline { private struct SegmentLengthInfo { public const int SUBSEGMENT_COUNT = 32; public double[] distances; public readonly double Total => distances[31]; } [CompilerGenerated] private sealed class d__14 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private PolylineProgress <>2__current; private int <>l__initialThreadId; private IEnumerable steps; public IEnumerable <>3__steps; public Spline <>4__this; private int 5__2; private double 5__3; private IEnumerator <>7__wrap3; PolylineProgress IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__14(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || (uint)(num - 1) <= 1u) { try { } finally { <>m__Finally1(); } } <>7__wrap3 = null; <>1__state = -2; } private bool MoveNext() { bool result; try { int num = <>1__state; Spline spline = <>4__this; switch (num) { default: result = false; goto end_IL_0000; case 0: <>1__state = -1; 5__2 = 0; 5__3 = 0.0; <>7__wrap3 = steps.GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; result = false; <>m__Finally1(); goto end_IL_0000; case 2: <>1__state = -3; break; } if (<>7__wrap3.MoveNext()) { double current = <>7__wrap3.Current; double num2 = 5__3 + current; while (true) { if (num2 > spline.SegmentLength(5__2)) { if (5__2 == spline.SegmentCount - 1) { <>2__current = 1.0; <>1__state = 1; result = true; break; } num2 -= spline.SegmentLength(5__2); 5__2++; continue; } 5__3 = num2; <>2__current = new PolylineProgress(spline.SegmentCount, 5__2, spline.InverseEvaluate(5__2, 5__3)); <>1__state = 2; result = true; break; } } else { <>m__Finally1(); <>7__wrap3 = null; result = false; } end_IL_0000:; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } return result; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap3 != null) { <>7__wrap3.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__14 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__14(0) { <>4__this = <>4__this }; } d__.steps = <>3__steps; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } private readonly ISplineImpl impl; private readonly SegmentLengthInfo[] segmentLengths; public int SegmentCount => impl.SegmentCount; public double Length => segmentLengths.Sum((SegmentLengthInfo lengthInfo) => lengthInfo.Total); public Spline(ISplineImpl impl) { this.impl = impl; segmentLengths = new SegmentLengthInfo[this.impl.SegmentCount]; for (int i = 0; i < segmentLengths.Length; i++) { segmentLengths[i] = CalculateSegmentLength(i); } } public Rfloat2 EvaluatePosition(PolylineProgress pos) { return impl.EvaluatePosition(in pos); } public Rfloat2 EvaluateTangent(PolylineProgress pos) { return impl.EvaluateTangent(in pos); } public Rfloat2 EvaluateAcceleration(PolylineProgress pos) { return impl.EvaluateAcceleration(in pos); } private SegmentLengthInfo CalculateSegmentLength(int index) { SegmentLengthInfo segmentLengthInfo = default(SegmentLengthInfo); segmentLengthInfo.distances = new double[32]; SegmentLengthInfo result = segmentLengthInfo; Rfloat2[] array = new Rfloat2[33]; for (int i = 0; i < 33; i++) { int num = i; ISplineImpl splineImpl = impl; PolylineProgress pos = new PolylineProgress(SegmentCount, index, 1.0 / 32.0 * (double)i); array[num] = splineImpl.EvaluatePosition(in pos); } double num2 = 0.0; for (int j = 0; j < 32; j++) { num2 += (array[j + 1] - array[j]).Length; result.distances[j] = num2; } return result; } private double SegmentLength(int index) { if (index < 0 || index > segmentLengths.Length - 1) { throw new IndexOutOfRangeException(); } return segmentLengths[index].Total; } private double InverseEvaluate(int segmentIndex, double segmentDistance) { SegmentLengthInfo segmentLengthInfo = segmentLengths[segmentIndex]; if (segmentDistance <= 0.0) { return 0.0; } for (int i = 0; i < segmentLengthInfo.distances.Length; i++) { if (!(segmentDistance > segmentLengthInfo.distances[i])) { double num = Rmath.InverseLerp(segmentDistance, (i > 0) ? segmentLengthInfo.distances[i - 1] : 0.0, segmentLengthInfo.distances[i]); return ((double)i + num) / 32.0; } } return 1.0; } [IteratorStateMachine(typeof(d__14))] public IEnumerable Walk(IEnumerable steps) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__14(-2) { <>4__this = this, <>3__steps = steps }; } } public static class SplineWalk { public class StepEmitter : IEnumerable, IEnumerable { private struct Enumerator : IEnumerator, IEnumerator, IDisposable { private readonly StepEmitter emitter; private int count; public double Current { get { if (count <= 0) { return 0.0; } return emitter.step; } } object IEnumerator.Current => Current; public Enumerator(StepEmitter emitter) { this.emitter = emitter; count = -1; } public void Dispose() { } public bool MoveNext() { count++; return true; } public void Reset() { count = -1; } } private double step; public StepEmitter(double step) { this.step = step; } public void SetStep(double value) { step = value; } public IEnumerator GetEnumerator() { return new Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public static IEnumerable Walk(this Spline spline, double step) { return spline.Walk(new StepEmitter(step)); } public static IEnumerable<(PolylineProgress, StepEmitter)> Walk(this Spline spline) { StepEmitter generator = new StepEmitter(0.0); return from pos in spline.Walk(generator) select (pos, generator); } } public static class Tangents { public static Rfloat2[] FiniteDifference(IList points, double tension = 0.5, double alpha = 0.5) { Rfloat2[] array = new Rfloat2[points.Count]; double[] array2 = new double[points.Count - 1]; for (int i = 0; i < points.Count - 1; i++) { array2[i] = Rmath.Pow(Rfloat2.Distance(points[i + 1], points[i]), alpha); } for (int j = 0; j < points.Count - 1; j++) { if (j == 0) { array[j] = points[j + 1] - points[j]; } else if (j == points.Count - 1) { array[j] = points[j] - points[j - 1]; } else { Rfloat2 rfloat = (points[j] - points[j - 1]) / array2[j - 1]; Rfloat2 rfloat2 = (points[j + 1] - points[j]) / array2[j]; array[j] = (rfloat + rfloat2) * (array2[j] + array2[j - 1]) / 2.0; } array[j] *= 1.0 - tension; } return array; } public static Rfloat2[] FiniteDifferenceAccelerations(IList knots, double tension = 0.5) { Rfloat2[] array = new Rfloat2[knots.Count]; for (int i = 0; i < knots.Count; i++) { if (i == 0) { array[i] = Rfloat2.Zero; } else if (i == knots.Count - 1) { array[i] = Rfloat2.Zero; } else { Rfloat2 rfloat = knots[i - 1] - knots[i]; Rfloat2 rfloat2 = knots[i + 1] - knots[i]; array[i] = rfloat + rfloat2; } array[i] *= 2.0 * tension; } return array; } } } namespace Riverheim.Util.Random { public static class Distribution { private const double CLAMPING = 1E-07; public static double Logistic(this RandomBox32 box, double mean, double temperature) { double num = box.Range(1E-07, 0.9999999); return mean + temperature * Rmath.Log(num / (1.0 - num)); } public static double Gumbel(this RandomBox32 box, double mean, double temperature) { double value = box.Range(1E-07, 0.9999999); return mean - temperature * Rmath.Log(0.0 - Rmath.Log(value)); } } public static class HashTools { private const short INLINE = 256; public static uint IntHash32(uint seed, uint x) { return Murmur32Mix(seed, x); } public static uint IntHash32(uint seed, int x, int y) { return Murmur32Mix(seed, PairMix32(x, y)); } public static uint IntHash32(uint seed, int x, int y, int z) { return Murmur32Mix(seed, PairMix32(x, (int)PairMix32(y, z))); } public static uint IntHash32(uint seed, Rint2 pos) { return IntHash32(seed, pos.x, pos.y); } public static ulong IntHash64(uint seed, ulong x) { return Murmur64Mix(seed, x); } public static ulong IntHash64(uint seed, int x, int y) { return Murmur64Mix(seed, PairMix64(x, y)); } public static ulong IntHash64(uint seed, Rint2 pos) { return IntHash64(seed, pos.x, pos.y); } public static uint StringHash32(string s) { return JenkinsOAATHash(0u, s); } public static uint StringHash32(uint seed, string s) { return JenkinsOAATHash(seed, s); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Murmur32Mix(uint seed, uint x) { x ^= seed; x *= 2654435769u; return Murmur32Mix(x); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint Murmur32Mix(uint x) { x ^= x >> 16; x *= 2246822507u; x ^= x >> 13; x *= 3266489909u; x ^= x >> 16; return x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Murmur64Mix(uint seed, ulong x) { x ^= seed; x *= 11400714819323198485uL; return Murmur64Mix(x); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong Murmur64Mix(ulong x) { x ^= x >> 33; x *= 18397679294719823053uL; x ^= x >> 33; x *= 14181476777654086739uL; x ^= x >> 33; return x; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint PairMix32(int x, int y) { return (uint)((x * -1640531527) ^ (y * 1852272827)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static ulong PairMix64(int x, int y) { return (ulong)((uint)x | ((long)y << 32)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static uint JenkinsOAATHash(uint seed, string s) { uint num = seed; foreach (char c in s) { num += c; num += num << 10; num ^= num >> 6; } num += num << 3; num ^= num >> 11; return num + (num << 15); } } public class RandomDirectory { private readonly uint seed; public string Path { get; } public override string ToString() { return $"RandomDirectory({seed}:{Path})"; } public RandomDirectory(int seed) : this((uint)seed, "") { } private RandomDirectory(uint seed, string path) { this.seed = seed; Path = path; } private uint Seed(string suffix) { return HashTools.StringHash32(seed, Path + "/" + suffix); } public RandomDirectory Sub(string path) { return new RandomDirectory(seed, Path + "/" + path); } public RandomDirectory Abs(string absPath) { return new RandomDirectory(seed, "/" + absPath); } public RandomDirectory Root() { return new RandomDirectory(seed, ""); } public RandomBox32 Single(string suffix) { return new RandomBox32(Seed(suffix)); } public RandomBox64 Double(string suffix) { return new RandomBox64(HashTools.IntHash64(Seed(suffix), 6364136223846793005uL)); } public RandomSequence Sequence(string suffix) { return new RandomSequence(Seed(suffix)); } public RandomMultiSequence MultiSequence(string suffix) { return new RandomMultiSequence(Seed(suffix)); } public RandomIndexer Indexer(string suffix) { return new RandomIndexer(Seed(suffix)); } public NoiseField Noise2D(string suffix, NoiseField.Config config) { return NoiseField.Create(Single(suffix).Bytes, config); } public VectorNoiseField WarpNoise(string suffix, double period, VectorNoiseField.RelativeWarpConfig config) { return VectorNoiseField.PerlinWarp(Single(suffix).Bytes, period, config); } public VectorNoiseField WarpNoise(string suffix, VectorNoiseField.AbsoluteWarpConfig config) { return VectorNoiseField.PerlinWarp(Single(suffix).Bytes, config); } public VoronoiCellIdNoise CellNoise(string suffix, double featureSize, double displacement = 1.0) { return new VoronoiCellIdNoise(Single(suffix).Bytes, 1.0 / featureSize, displacement); } public NoiseField3d Noise3D(string suffix, double featureSize) { return NoiseField3d.Create(featureSize, Single(suffix).Bytes); } } public static class RandomDirectoryExtensions { public static NoiseField Noise2D(this RandomDirectory directory, string suffix, double period) { return directory.Noise2D(suffix, new NoiseField.Config { type = NoiseField.Config.Type.Gradient, Period = period, amplitude = 1.0 }); } public static NoiseField BiasedNoise2D(this RandomDirectory directory, string suffix, double period) { return directory.Noise2D(suffix, new NoiseField.Config { type = NoiseField.Config.Type.Gradient, Period = period, amplitude = 0.5, bias = 0.5 }); } public static NoiseField CellularNoise2D(this RandomDirectory directory, string suffix, double period, double intensity = 1.0) { return directory.Noise2D(suffix, new NoiseField.Config { type = NoiseField.Config.Type.Cellular, Period = period, amplitude = 1.0, displacement = intensity }); } public static NoiseField CellularNoise2DDiv(this RandomDirectory directory, string suffix, double period, double intensity = 1.0) { return directory.Noise2D(suffix, new NoiseField.Config { type = NoiseField.Config.Type.CellularDiv, Period = period, amplitude = 1.0, displacement = intensity }); } } public class RandomSet { private readonly RandomSequence random; private readonly IndexedSet items = new IndexedSet(); public int Count => items.Count; public RandomSet(RandomSequence random) { this.random = random; } public RandomSet(RandomSequence random, IEnumerable collection) : this(random) { foreach (T item in collection) { Add(item); } } public void Add(T item) { items.Add(item); } public void Remove(T item) { items.Remove(item); } public bool Contains(T item) { return items.Contains(item); } public T Sample() { if (items.Count != 0) { return items.Get(random.Single.Range(items.Count)); } throw new KeyNotFoundException("attempt to sample random value from an empty set!"); } public T Pop() { T val = Sample(); Remove(val); return val; } } public static class WeightedRand { public interface IItem { uint Weight { get; } } public static T Sample(RandomBox32 random, ICollection items) where T : IItem { uint num = 0u; foreach (T item in items) { num += item.Weight; } return Sample(random, items, num); } public static T Sample(RandomBox32 random, IEnumerable items, uint sum) where T : IItem { uint num = (uint)random.Range((int)sum); foreach (T item in items) { if (item.Weight > num) { return item; } num -= item.Weight; } throw new KeyNotFoundException("attempt to sample random value from an empty set!"); } public static T Sample(RandomBox32 random, IList items, Func weightFunc) { if (items.Count == 0) { throw new KeyNotFoundException("attempt to sample random value from an empty set!"); } double[] array = items.Select(weightFunc).ToArray(); double num = random.Range(array.Sum()); for (int i = 0; i < items.Count; i++) { double num2 = array[i]; if (num2 > num) { return items[i]; } num -= num2; } return items[items.Count - 1]; } } } namespace Riverheim.Util.Random.Primitives { public readonly struct RandomBox32 { private const uint UNIT_EXPONENT = 1065353216u; private const int NON_FRACT_BIT_COUNT = 9; private readonly uint state; public uint Bytes => state; public double Value => AsFloat01(); private static float Utof(uint u) { return BitConverter.Int32BitsToSingle((int)u); } public RandomBox32(uint state) { this.state = state; } private float AsFloat01() { return Utof(0x3F800000u | (state >> 9)) - 1f; } private int AsInt(int offset, uint range) { return offset + (int)((long)state * (long)range >>> 32); } public double Range(double max) { return (double)AsFloat01() * max; } public double Range(double min, double max) { return min + (double)AsFloat01() * (max - min); } public double Range(Rmath.Range range) { return Range(range.min, range.max); } public int Range(int max) { return AsInt(0, (uint)max); } public int Range(int min, int max) { return AsInt(min, (uint)(max - min)); } } public readonly struct RandomBox64 { private readonly RandomBox32 box1; private readonly RandomBox32 box2; public Rfloat2 Value => new Rfloat2(box1.Value, box2.Value); public RandomBox64(uint state1, uint state2) { box1 = new RandomBox32(state1); box2 = new RandomBox32(state2); } public RandomBox64(ulong state) { box1 = new RandomBox32((uint)(state & 0xFFFFFFFFu)); box2 = new RandomBox32((uint)(state >> 32)); } public Rfloat2 Range(double max) { return new Rfloat2(box1.Range(max), box2.Range(max)); } public Rfloat2 Range(double min, double max) { return new Rfloat2(box1.Range(min, max), box2.Range(min, max)); } public Rfloat2 Range(Rmath.Range range) { return new Rfloat2(box1.Range(range), box2.Range(range)); } public Rint2 Range(int max) { return new Rint2(box1.Range(max), box2.Range(max)); } public Rint2 Range(int min, int max) { return new Rint2(box1.Range(min, max), box2.Range(min, max)); } } public class RandomIndexer { private readonly uint seed; private const uint HOP32 = 1103515245u; private const ulong HOP64 = 6364136223846793005uL; public RandomIndexer(uint seed) { this.seed = seed; } public RandomBox32 Single(int index) { return new RandomBox32(HashTools.IntHash32(seed, (uint)(index * 1103515245))); } public RandomBox32 Single(int x, int y) { return new RandomBox32(HashTools.IntHash32(seed, x, y)); } public RandomBox32 Single(Rint2 index) { return new RandomBox32(HashTools.IntHash32(seed, index)); } public RandomBox64 Double(int index) { return new RandomBox64(HashTools.IntHash64(seed, (ulong)(index * 6364136223846793005L))); } public RandomBox64 Double(int x, int y) { return new RandomBox64(HashTools.IntHash64(seed, x, y)); } public RandomBox64 Double(Rint2 index) { return new RandomBox64(HashTools.IntHash64(seed, index)); } } public class RandomMultiSequence { private readonly uint seed; private readonly Dictionary states; private const uint HOP = 1103515245u; public RandomMultiSequence(uint seed) { this.seed = seed; states = new Dictionary(); } private int GetAndAdvance(int id) { if (!states.TryGetValue(id, out var value)) { value = 0u; } states[id] = value + 1103515245; return (int)value; } public RandomBox32 Single(int id) { return new RandomBox32((uint)HashTools.IntHash64(seed, id, GetAndAdvance(id))); } public RandomBox64 Double(int id) { return new RandomBox64(HashTools.IntHash64(seed, id, GetAndAdvance(id))); } } public class RandomSequence { private readonly uint seed; private ulong state; private const ulong HOP = 6364136223846793005uL; public RandomBox32 Single => new RandomBox32((uint)HashTools.IntHash64(seed, state += 6364136223846793005uL)); public RandomBox64 Double => new RandomBox64(HashTools.IntHash64(seed, state += 6364136223846793005uL)); public RandomSequence(uint seed) { this.seed = seed; state = 0uL; } } } namespace Riverheim.Util.Random.Noise { public class NoiseField { private delegate double NoiseFunc(uint seed, Rfloat2 pos); [Serializable] public struct Config { public enum Type { Gradient, Cellular, CellularDiv } public Type type; public double frequency; public double amplitude; public double bias; public double displacement; public FractalConfig fractal; public double Period { get { if (frequency != 0.0) { return 1.0 / frequency; } return 0.0; } set { frequency = ((value == 0.0) ? 0.0 : (1.0 / value)); } } } [Serializable] public struct FractalConfig { public enum FractalType { Fbm, Max, Min } public FractalType type; public int octaves; public double lacunarity; public double gain; public double smoothing; public bool IsTrivial => octaves <= 1; public double GetMultiplier() { if (type != 0) { return 1.0; } double num = 0.0; double num2 = 1.0; for (int i = 0; i < octaves; i++) { num += num2; num2 *= gain; } return num; } } private readonly uint seed; private readonly NoiseFunc func; private readonly Config config; private NoiseField(uint seed, Config config) { this.seed = seed; this.config = config; func = BaseFunc(config, RandomOffset(seed)); if (!config.fractal.IsTrivial) { func = Fractal(config.fractal, func); } } public double Sample(Rfloat2 pos) { return func(seed, pos); } public static NoiseField Create(uint seed, Config config) { return new NoiseField(seed, config); } private static NoiseFunc BaseFunc(Config config, Rfloat2 offset) { if (config.frequency == 0.0 || config.amplitude == 0.0) { return (uint _, Rfloat2 _) => 0.0; } return config.type switch { Config.Type.Gradient => (uint seed, Rfloat2 pos) => config.bias + config.amplitude * GradientNoise.Sample(seed, config.frequency * pos.x + offset.x, config.frequency * pos.y + offset.y), Config.Type.Cellular => (uint seed, Rfloat2 pos) => config.bias + config.amplitude * VoronoiNoise.SampleSimple(seed, config.frequency * pos.x + offset.x, config.frequency * pos.y + offset.y, config.displacement), Config.Type.CellularDiv => delegate(uint seed, Rfloat2 pos) { VoronoiNoise.Result result = VoronoiNoise.Sample(seed, config.frequency * pos.x + offset.x, config.frequency * pos.y + offset.y, config.displacement); return config.bias + config.amplitude * result.d1Sqr / result.d2Sqr; }, _ => throw new ArgumentOutOfRangeException(), }; } private static NoiseFunc Fractal(FractalConfig fractal, NoiseFunc func) { return fractal.type switch { FractalConfig.FractalType.Fbm => (uint seed, Rfloat2 pos) => FbmFractal(seed, func, pos, 1.0 / fractal.GetMultiplier(), fractal.octaves, fractal.lacunarity, fractal.gain), FractalConfig.FractalType.Max => (uint seed, Rfloat2 pos) => MaxFractal(seed, func, pos, fractal.octaves, fractal.lacunarity, fractal.gain, fractal.smoothing), FractalConfig.FractalType.Min => (uint seed, Rfloat2 pos) => MinFractal(seed, func, pos, fractal.octaves, fractal.lacunarity, fractal.gain, fractal.smoothing), _ => throw new ArgumentOutOfRangeException(), }; } public NoiseField Fractal(FractalConfig fractal) { Config config = this.config; config.fractal = fractal; return new NoiseField(seed, config); } public NoiseField Fractal(int octaves) { return Fractal(new FractalConfig { octaves = octaves, gain = 0.5, lacunarity = 2.0 }); } private static double FbmFractal(uint seed, NoiseFunc func, Rfloat2 pos, double amplitude, int octaves, double lacunarity, double gain) { double num = 0.0; double num2 = 1.0; for (uint num3 = 0u; num3 < octaves; num3++) { num += amplitude * func(seed + num3, pos * num2); num2 *= lacunarity; amplitude *= gain; } return num; } private static double MaxFractal(uint seed, NoiseFunc func, Rfloat2 pos, int octaves, double lacunarity, double gain, double smoothing) { if (octaves == 0) { return 0.0; } double num = double.MinValue; double num2 = 1.0; double num3 = 1.0; for (uint num4 = 0u; num4 < octaves; num4++) { double b = num3 * func(seed + num4, pos * num2); num = ((smoothing > 0.0) ? SmoothMax.Cubic(num, b, smoothing) : Rmath.Max(num, b)); num2 *= lacunarity; num3 *= gain; } return num; } private static double MinFractal(uint seed, NoiseFunc func, Rfloat2 pos, int octaves, double lacunarity, double gain, double smoothing) { if (octaves == 0) { return 0.0; } double num = double.MaxValue; double num2 = 1.0; double num3 = 1.0; for (uint num4 = 0u; num4 < octaves; num4++) { double b = num3 * func(seed + num4, pos * num2); num = ((smoothing > 0.0) ? SmoothMin.Cubic(num, b, smoothing) : Rmath.Min(num, b)); num2 *= lacunarity; num3 *= gain; } return num; } public static NoiseField operator *(NoiseField noise, double scalar) { Config config = noise.config; config.amplitude *= scalar; config.bias *= scalar; return new NoiseField(noise.seed, config); } public static NoiseField operator *(double scalar, NoiseField noise) { Config config = noise.config; config.amplitude *= scalar; config.bias *= scalar; return new NoiseField(noise.seed, config); } public static NoiseField operator /(NoiseField noise, double scalar) { Config config = noise.config; config.amplitude /= scalar; config.bias /= scalar; return new NoiseField(noise.seed, config); } public static NoiseField operator +(NoiseField noise, double scalar) { Config config = noise.config; config.bias += scalar; return new NoiseField(noise.seed, config); } public static NoiseField operator +(double scalar, NoiseField noise) { Config config = noise.config; config.bias += scalar; return new NoiseField(noise.seed, config); } public static NoiseField operator -(NoiseField noise, double scalar) { Config config = noise.config; config.bias -= scalar; return new NoiseField(noise.seed, config); } public static NoiseField operator -(double scalar, NoiseField noise) { Config config = noise.config; config.amplitude *= -1.0; config.bias = scalar - config.bias; return new NoiseField(noise.seed, config); } public static NoiseField operator -(NoiseField noise) { Config config = noise.config; config.amplitude *= -1.0; return new NoiseField(noise.seed, config); } public static Rfloat2 RandomOffset(uint seed) { return new RandomBox64(seed + 3735928559u).Value; } } public class NoiseField3d { private delegate double NoiseFunc(uint seed, Rfloat3 pos); private readonly uint seed; private readonly NoiseFunc func; private NoiseField3d(uint seed, NoiseFunc func) { this.seed = seed; this.func = func; } public double Sample(Rfloat3 pos) { return func(seed, pos); } public static NoiseField3d Flat() { return new NoiseField3d(0u, (uint _, Rfloat3 _) => 0.0); } public static NoiseField3d Perlin(double frequency, uint seed) { Rfloat3 offset = RandomOffset(seed); return new NoiseField3d(seed, (uint s, Rfloat3 pos) => GradientNoise.Sample3D(s, frequency * pos.x + offset.x, frequency * pos.y + offset.y, frequency * pos.z + offset.z)); } public static NoiseField3d Create(double period, uint seed) { if (period != 0.0) { return Perlin(1.0 / period, seed); } return Flat(); } private static Rfloat3 RandomOffset(uint seed) { return new Rfloat3(new RandomBox32(seed + 3735928559u).Value, new RandomBox32((uint)((int)seed + -559038737 + 1)).Value, new RandomBox32((uint)((int)seed + -559038737 + 2)).Value); } } public readonly struct VectorNoiseField { private delegate Rfloat2 NoiseFunc(uint seed, Rfloat2 pos); [Serializable] public struct RelativeWarpConfig { public double frequency; public double intensity; } [Serializable] public struct AbsoluteWarpConfig { public double period; public double intensity; } private readonly uint seed; private readonly NoiseFunc func; private VectorNoiseField(uint seed, NoiseFunc func) { this.seed = seed; this.func = func; } public Rfloat2 Sample(Rfloat2 pos) { return func(seed, pos); } public static VectorNoiseField Flat() { return new VectorNoiseField(0u, (uint _, Rfloat2 _) => Rfloat2.Zero); } public static VectorNoiseField Perlin(uint seed, double frequency, double amplitude) { Rfloat2 offset = NoiseField.RandomOffset(seed); return new VectorNoiseField(seed, (uint s, Rfloat2 pos) => amplitude * GradientNoise.SampleVector(s, frequency * pos.x + offset.x, frequency * pos.y + offset.y)); } public static VectorNoiseField PerlinWarp(uint seed, double period, RelativeWarpConfig config) { if (period != 0.0) { return Perlin(seed, config.frequency / period, config.intensity * period); } return Flat(); } public static VectorNoiseField PerlinWarp(uint seed, AbsoluteWarpConfig config) { if (config.period != 0.0) { return Perlin(seed, 1.0 / config.period, config.intensity * config.period); } return Flat(); } } public class VoronoiCellIdNoise { private readonly uint seed; private readonly double frequency; private readonly double displacement; private readonly Rfloat2 offset; public VoronoiCellIdNoise(uint seed, double frequency, double displacement = 1.0) { this.seed = seed; this.frequency = frequency; this.displacement = displacement; offset = NoiseField.RandomOffset(seed); } public int Sample(Rfloat2 pos) { return (int)VoronoiNoise.ClosestCell(seed, frequency * pos.x + offset.x, frequency * pos.y + offset.y, displacement); } } } namespace Riverheim.Util.Random.Noise.Primitives { public static class GradientNoise { private const short INLINE = 256; private static readonly Rfloat2[] Gradients2D = new Rfloat2[16] { new Rfloat2(0.9807852804032304, 0.1950903220161283), new Rfloat2(0.8314696123025452, 0.5555702330196022), new Rfloat2(0.5555702330196022, 0.8314696123025452), new Rfloat2(0.1950903220161283, 0.9807852804032304), new Rfloat2(-0.1950903220161283, 0.9807852804032304), new Rfloat2(-0.5555702330196022, 0.8314696123025452), new Rfloat2(-0.8314696123025452, 0.5555702330196022), new Rfloat2(-0.9807852804032304, 0.1950903220161283), new Rfloat2(-0.9807852804032304, -0.1950903220161283), new Rfloat2(-0.8314696123025452, -0.5555702330196022), new Rfloat2(-0.5555702330196022, -0.8314696123025452), new Rfloat2(-0.1950903220161283, -0.9807852804032304), new Rfloat2(0.1950903220161283, -0.9807852804032304), new Rfloat2(0.5555702330196022, -0.8314696123025452), new Rfloat2(0.8314696123025452, -0.5555702330196022), new Rfloat2(0.9807852804032304, -0.1950903220161283) }; private static readonly Rfloat3[] Gradients3D = new Rfloat3[32] { new Rfloat3(0.577350269189626, 0.577350269189626, 0.577350269189626), new Rfloat3(0.577350269189626, 0.577350269189626, -0.577350269189626), new Rfloat3(0.577350269189626, -0.577350269189626, 0.577350269189626), new Rfloat3(0.577350269189626, -0.577350269189626, -0.577350269189626), new Rfloat3(-0.577350269189626, 0.577350269189626, 0.577350269189626), new Rfloat3(-0.577350269189626, 0.577350269189626, -0.577350269189626), new Rfloat3(-0.577350269189626, -0.577350269189626, 0.577350269189626), new Rfloat3(-0.577350269189626, -0.577350269189626, -0.577350269189626), new Rfloat3(0.0, 0.35682208977309, 0.934172358962716), new Rfloat3(0.0, 0.35682208977309, -0.934172358962716), new Rfloat3(0.0, -0.35682208977309, 0.934172358962716), new Rfloat3(0.0, -0.35682208977309, -0.934172358962716), new Rfloat3(0.35682208977309, 0.934172358962716, 0.0), new Rfloat3(0.35682208977309, -0.934172358962716, 0.0), new Rfloat3(-0.35682208977309, 0.934172358962716, 0.0), new Rfloat3(-0.35682208977309, -0.934172358962716, 0.0), new Rfloat3(0.934172358962716, 0.0, 0.35682208977309), new Rfloat3(0.934172358962716, 0.0, -0.35682208977309), new Rfloat3(-0.934172358962716, 0.0, 0.35682208977309), new Rfloat3(-0.934172358962716, 0.0, -0.35682208977309), new Rfloat3(0.525731112119134, 0.0, 0.85065080835204), new Rfloat3(0.85065080835204, 0.525731112119134, 0.0), new Rfloat3(0.0, 0.85065080835204, 0.525731112119134), new Rfloat3(0.525731112119134, 0.0, -0.85065080835204), new Rfloat3(0.0, 0.85065080835204, -0.525731112119134), new Rfloat3(0.0, -0.85065080835204, 0.525731112119134), new Rfloat3(0.85065080835204, -0.525731112119134, 0.0), new Rfloat3(0.0, -0.85065080835204, -0.525731112119134), new Rfloat3(-0.85065080835204, 0.525731112119134, 0.0), new Rfloat3(-0.525731112119134, 0.0, 0.85065080835204), new Rfloat3(-0.525731112119134, 0.0, -0.85065080835204), new Rfloat3(-0.85065080835204, -0.525731112119134, 0.0) }; [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Rfloat2 Grad(uint hash) { return Gradients2D[hash & 0xF]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double Grad(uint seed, int x, int y, double u, double v) { Rfloat2 rfloat = Grad(HashTools.IntHash32(seed, x, y)); return rfloat.x * u + rfloat.y * v; } public static double Sample(uint seed, double x, double y) { int num = Rmath.FloorToInt(x); int num2 = Rmath.FloorToInt(y); x -= (double)num; y -= (double)num2; double t = Rmath.Smooth5(x); double t2 = Rmath.Smooth5(y); return 1.43815 * Rmath.UnclampedLerp(t2, Rmath.UnclampedLerp(t, Grad(seed, num, num2, x, y), Grad(seed, num + 1, num2, x - 1.0, y)), Rmath.UnclampedLerp(t, Grad(seed, num, num2 + 1, x, y - 1.0), Grad(seed, num + 1, num2 + 1, x - 1.0, y - 1.0))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static Rfloat2 VGrad(uint seed, int x, int y, double u, double v) { uint num = HashTools.IntHash32(seed, x, y); Rfloat2 rfloat = Grad(num); Rfloat2 rfloat2 = Grad(num >> 4); return new Rfloat2(rfloat.x * u + rfloat.y * v, rfloat2.x * u + rfloat2.y * v); } public static Rfloat2 SampleVector(uint seed, double x, double y) { int num = Rmath.FloorToInt(x); int num2 = Rmath.FloorToInt(y); x -= (double)num; y -= (double)num2; double t = Rmath.Smooth5(x); double t2 = Rmath.Smooth5(y); return 1.43815 * Rfloat2.UnclampedLerp(t2, Rfloat2.UnclampedLerp(t, VGrad(seed, num, num2, x, y), VGrad(seed, num + 1, num2, x - 1.0, y)), Rfloat2.UnclampedLerp(t, VGrad(seed, num, num2 + 1, x, y - 1.0), VGrad(seed, num + 1, num2 + 1, x - 1.0, y - 1.0))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double WiggledGrad(uint seed, int x, int y, double u, double v) { uint num = HashTools.IntHash32(seed, x, y); Rfloat2 rfloat = Gradients2D[num & 0xF]; Rfloat2 rfloat2 = Gradients2D[(num >> 4) & 0xF]; return rfloat.x * (u + 0.225 * rfloat2.x) + rfloat.y * (v + 0.225 * rfloat2.y); } public static double SampleWiggled(uint seed, double x, double y) { int num = Rmath.FloorToInt(x); int num2 = Rmath.FloorToInt(y); x -= (double)num; y -= (double)num2; double t = Rmath.Smooth5(x); double t2 = Rmath.Smooth5(y); return 1.10822 * Rmath.UnclampedLerp(t2, Rmath.UnclampedLerp(t, WiggledGrad(seed, num, num2, x, y), WiggledGrad(seed, num + 1, num2, x - 1.0, y)), Rmath.UnclampedLerp(t, WiggledGrad(seed, num, num2 + 1, x, y - 1.0), WiggledGrad(seed, num + 1, num2 + 1, x - 1.0, y - 1.0))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double Grad(uint seed, int x, int y, int z, double u, double v, double w) { Rfloat3 rfloat = Gradients3D[HashTools.IntHash32(seed, x, y, z) & 0x1F]; return rfloat.x * u + rfloat.y * v + rfloat.z * w; } public static double Sample3D(uint seed, double x, double y, double z) { int num = Rmath.FloorToInt(x); int num2 = Rmath.FloorToInt(y); int num3 = Rmath.FloorToInt(z); x -= (double)num; y -= (double)num2; z -= (double)num3; double t = Rmath.Smooth5(x); double t2 = Rmath.Smooth5(y); double t3 = Rmath.Smooth5(z); return 1.21718 * Rmath.UnclampedLerp(t3, Rmath.UnclampedLerp(t2, Rmath.UnclampedLerp(t, Grad(seed, num, num2, num3, x, y, z), Grad(seed, num + 1, num2, num3, x - 1.0, y, z)), Rmath.UnclampedLerp(t, Grad(seed, num, num2 + 1, num3, x, y - 1.0, z), Grad(seed, num + 1, num2 + 1, num3, x - 1.0, y - 1.0, z))), Rmath.UnclampedLerp(t2, Rmath.UnclampedLerp(t, Grad(seed, num, num2, num3 + 1, x, y, z - 1.0), Grad(seed, num + 1, num2, num3 + 1, x - 1.0, y, z - 1.0)), Rmath.UnclampedLerp(t, Grad(seed, num, num2 + 1, num3 + 1, x, y - 1.0, z - 1.0), Grad(seed, num + 1, num2 + 1, num3 + 1, x - 1.0, y - 1.0, z - 1.0)))); } } public static class VoronoiNoise { public struct Result { public double d1Sqr; public double d2Sqr; } private static readonly Rfloat2[] Displacements = PrecomputeDisplacements(); private static Rfloat2[] PrecomputeDisplacements() { Rfloat2[] array = new Rfloat2[64]; for (int i = 0; i < 64; i++) { double value = (double)i * 2.0 / 64.0 * Math.PI; array[i] = 0.49 * new Rfloat2(Rmath.Cos(value), Rmath.Sin(value)); } return array; } public static double SampleSimple(uint seed, double x, double y, double displacementMult = 1.0) { int num = Rmath.FloorToInt(x); int num2 = Rmath.FloorToInt(y); x -= (double)num + 0.5; y -= (double)num2 + 0.5; double num3 = double.MaxValue; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { int x2 = num + j; int y2 = num2 + i; Rfloat2 rfloat = displacementMult * Displacements[HashTools.IntHash32(seed, x2, y2) & 0x3F]; double num4 = (double)j - x + rfloat.x; double num5 = (double)i - y + rfloat.y; double num6 = num4 * num4 + num5 * num5; if (num6 < num3) { num3 = num6; } } } return num3; } public static uint ClosestCell(uint seed, double x, double y, double displacementMult = 1.0) { int num = Rmath.FloorToInt(x); int num2 = Rmath.FloorToInt(y); x -= (double)num + 0.5; y -= (double)num2 + 0.5; uint result = 0u; double num3 = double.MaxValue; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { int x2 = num + j; int y2 = num2 + i; uint num4 = HashTools.IntHash32(seed, x2, y2); Rfloat2 rfloat = displacementMult * Displacements[num4 & 0x3F]; double num5 = (double)j - x + rfloat.x; double num6 = (double)i - y + rfloat.y; double num7 = num5 * num5 + num6 * num6; if (num7 < num3) { result = num4; num3 = num7; } } } return result; } public static Rfloat2 ClosestCellPosition(uint seed, double x, double y, double displacementMult = 1.0) { int num = Rmath.FloorToInt(x); int num2 = Rmath.FloorToInt(y); x -= (double)num + 0.5; y -= (double)num2 + 0.5; Rfloat2 result = Rfloat2.Zero; double num3 = double.MaxValue; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { int num4 = num + j; int num5 = num2 + i; uint num6 = HashTools.IntHash32(seed, num4, num5); Rfloat2 rfloat = displacementMult * Displacements[num6 & 0x3F]; double num7 = (double)j - x + rfloat.x; double num8 = (double)i - y + rfloat.y; double num9 = num7 * num7 + num8 * num8; if (num9 < num3) { result = new Rfloat2(num4, num5) + rfloat; num3 = num9; } } } return result; } public static Result Sample(uint seed, double x, double y, double displacementMult = 1.0) { int num = Rmath.FloorToInt(x); int num2 = Rmath.FloorToInt(y); x -= (double)num + 0.5; y -= (double)num2 + 0.5; double num3 = double.MaxValue; double num4 = double.MaxValue; for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { int x2 = num + j; int y2 = num2 + i; Rfloat2 rfloat = displacementMult * Displacements[HashTools.IntHash32(seed, x2, y2) & 0x3F]; double num5 = (double)j - x + rfloat.x; double num6 = (double)i - y + rfloat.y; double b = num5 * num5 + num6 * num6; num4 = Rmath.Max(Rmath.Min(num4, b), num3); num3 = Rmath.Min(num3, b); } } Result result = default(Result); result.d1Sqr = num3; result.d2Sqr = num4; return result; } } } namespace Riverheim.Util.Mathematics { public static class Functions { private const MethodImplOptions INLINE = MethodImplOptions.AggressiveInlining; [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Saturation(double x, double midpoint, double p) { double num = Rmath.Pow(Rmath.Max(0.0, x), p); return num / (num + Rmath.Pow(midpoint, p)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Sigmoid(double value, double temp = 1.0) { if (!(temp > 0.0)) { return 0.5; } return 1.0 / (1.0 + Rmath.Exp((0.0 - value) / temp)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double SoftSigmoid(double x, double temp = 1.0) { if (temp <= 0.0) { return 0.5; } x /= temp; return 0.5 * (1.0 + x / Rmath.Sqrt(4.0 + x * x)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Tanh(double value) { return 2.0 / (1.0 + Rmath.Exp(-2.0 * value)) - 1.0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Gauss(double x, double nSigmas) { return Gauss2(x * x, nSigmas); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Gauss2(double x2, double nSigmas) { return Rmath.Exp(-0.5 * nSigmas * nSigmas * x2); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double PolySinglet(double x) { return -8.0 * x * x * (x - 1.0) * (x - 1.0) * (2.0 * x - 1.0); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double ShapeStep(double x, double start, double end, double magnitude = 1.0) { return x + (end - start) * magnitude * PolySinglet(Rmath.InverseLerp(x, start, end)); } public static double[] Softmax(double[] values, double temperature = 1.0) { double num = double.MinValue; foreach (double num2 in values) { if (num < num2) { num = num2; } } double num3 = 1.0 / temperature; double num4 = 0.0; double[] array = new double[values.Length]; for (int j = 0; j < values.Length; j++) { array[j] = Rmath.Exp(num3 * (values[j] - num)); num4 += array[j]; } for (int k = 0; k < values.Length; k++) { array[k] /= num4; } return array; } } public static class Smoothing { public struct Val { public double x; public double dx; public Val(double x, double dx) { this.x = x; this.dx = dx; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Min(double a, double b, double k) { double num = Rmath.Min(a, b); if (k <= 0.0) { return num; } double num2 = k * 6.0; double num3 = Rmath.Max(0.0, num2 - Rmath.Abs(a - b)) / num2; return num - num3 * num3 * num3 * k; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Val Min(Val a, Val b, double k) { double num = Rmath.Min(a.x, b.x); if (k <= 0.0) { return new Val(num, (a.x < b.x) ? a.dx : b.dx); } double num2 = k * 6.0; double num3 = Rmath.Max(0.0, num2 - Rmath.Abs(a.x - b.x)) / num2; double num4 = num3 * num3; double t = ((a.x < b.x) ? (0.5 * num4) : (1.0 - 0.5 * num4)); return new Val(num - num3 * num4 * k, Rmath.UnclampedLerp(t, a.dx, b.dx)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Max(double a, double b, double k) { double num = Rmath.Max(a, b); if (k <= 0.0) { return num; } double num2 = k * 6.0; double num3 = Rmath.Max(0.0, num2 - Rmath.Abs(a - b)) / num2; return num + num3 * num3 * num3 * k; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Val Max(Val a, Val b, double k) { double num = Rmath.Max(a.x, b.x); if (k <= 0.0) { return new Val(num, (a.x > b.x) ? a.dx : b.dx); } double num2 = k * 6.0; double num3 = Rmath.Max(0.0, num2 - Rmath.Abs(a.x - b.x)) / num2; double num4 = num3 * num3; double t = ((a.x > b.x) ? (0.5 * num4) : (1.0 - 0.5 * num4)); return new Val(num + num3 * num4 * k, Rmath.UnclampedLerp(t, a.dx, b.dx)); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double ClampLower(double x, double k) { return x * x / (x + k) - 1.0 / (1.0 + k) + 1.0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double ClampUpper(double x, double k) { return 1.0 - ClampLower(1.0 - x, k); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double EaseIn(double x, double k) { return x * x * (1.0 + k) / (x + k); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double EaseOut(double x, double k) { return 1.0 - EaseIn(1.0 - x, k); } } } namespace Riverheim.Util.Interpolation { public class BarycentricInterpolator { private readonly Tiling tiling; private readonly TriangleMesh mesh; public BarycentricInterpolator(Tiling tiling, TriangleMesh mesh) { this.tiling = tiling; this.mesh = mesh; } public double Interpolate(Rfloat2 pos, TileData values, double defaultValue = 0.0) { BarycentricCoordinates bc; int triangleIndex = mesh.GetTriangleIndex(pos, out bc); if (triangleIndex == -1) { return defaultValue; } double num = 0.0; for (int i = 0; i < 3; i++) { int vertex = mesh.GetVertex(triangleIndex, i); num += bc[i] * ((vertex < tiling.Count) ? ((double)tiling.GetTile(vertex).Data(values)) : defaultValue); } return num; } public double InterpolateOnSubtilingData(Rfloat2 pos, TileData values, SubtilingMapping mapping, double defaultValue = 0.0) { BarycentricCoordinates bc; int triangleIndex = mesh.GetTriangleIndex(pos, out bc); if (triangleIndex == -1) { return defaultValue; } double num = 0.0; for (int i = 0; i < 3; i++) { int vertex = mesh.GetVertex(triangleIndex, i); double num2 = defaultValue; if (vertex < tiling.Count) { Tile? childTile = mapping.GetChildTile(tiling.GetTile(vertex)); if (childTile.HasValue) { Tile valueOrDefault = childTile.GetValueOrDefault(); num2 = values[valueOrDefault]; } } num += bc[i] * num2; } return num; } private BarycentricCoordinates WarpedBc(int tri, BarycentricCoordinates bc, double warp) { double num = bc.u * (1.0 + warp * GradientNoise.Sample((uint)mesh.GetVertex(tri, 0), 2.0 * bc.u, 0.5)); double num2 = bc.v * (1.0 + warp * GradientNoise.Sample((uint)mesh.GetVertex(tri, 1), 2.0 * bc.v, 0.5)); double num3 = bc.w * (1.0 + warp * GradientNoise.Sample((uint)mesh.GetVertex(tri, 2), 2.0 * bc.w, 0.5)); double num4 = 1.0 / (num + num2 + num3); return new BarycentricCoordinates(num2 * num4, num3 * num4); } public double Interpolate(Rfloat2 pos, double warp, TileData values, double defaultValue = 0.0) { BarycentricCoordinates bc; int triangleIndex = mesh.GetTriangleIndex(pos, out bc); if (triangleIndex == -1) { return defaultValue; } bc = WarpedBc(triangleIndex, bc, warp); double num = 0.0; for (int i = 0; i < 3; i++) { int vertex = mesh.GetVertex(triangleIndex, i); num += bc[i] * ((vertex < tiling.Count) ? ((double)tiling.GetTile(vertex).Data(values)) : defaultValue); } return num; } } public class BicubicInterpolator { public enum Method { CatmullRom, BSpline } private readonly Rint2 size; private readonly Rfloat2 scale; private readonly float[,] data; private readonly Rfloat4X4 matrix; public BicubicInterpolator(Rfloat2 range, Rfloat2 resolution, Func signal, Method method = Method.CatmullRom) : this(range, resolution, PrepareData(range, resolution, signal), method) { } public BicubicInterpolator(Rfloat2 range, Rfloat2 resolution, float[,] data, Method method = Method.CatmullRom) { size = (range / resolution).CeilToInt() + Rint2.One; scale = 1.0 / resolution; matrix = method switch { Method.CatmullRom => InterpolationTools.MatrixCatmullRom, Method.BSpline => InterpolationTools.MatrixBSpline, _ => throw new ArgumentOutOfRangeException("method"), }; this.data = data; } public static float[,] PrepareData(Rfloat2 range, Rfloat2 resolution, Func signal) { Rint2 size = (range / resolution).CeilToInt() + Rint2.One; float[,] result = new float[2 * size.x + 1, 2 * size.y + 1]; Parallel.For(0, result.GetLength(1), delegate(int j) { for (int i = 0; i < result.GetLength(0); i++) { result[i, j] = (float)signal(resolution * (new Rfloat2(i, j) - size)); } }); return result; } public double Interpolate(Rfloat2 pos, double defaultValue = 0.0) { Rint2 rint = (pos * scale).FloorToInt(); Rfloat2 t = pos * scale - rint; rint += size - Rint2.One; int x = rint.x; int y = rint.y; if (x < 0 || x >= 2 * size.x - 2) { return defaultValue; } if (y < 0 || y >= 2 * size.y - 2) { return defaultValue; } Rfloat4X4 f = new Rfloat4X4(data[x, y], data[x + 1, y], data[x + 2, y], data[x + 3, y], data[x, y + 1], data[x + 1, y + 1], data[x + 2, y + 1], data[x + 3, y + 1], data[x, y + 2], data[x + 1, y + 2], data[x + 2, y + 2], data[x + 3, y + 2], data[x, y + 3], data[x + 1, y + 3], data[x + 2, y + 3], data[x + 3, y + 3]); return InterpolationTools.Bicubic(t, in f, in matrix); } public double Interpolate(Rfloat2 pos, out Rfloat2 gradient, double defaultValue = 0.0) { gradient = Rfloat2.Zero; Rint2 rint = (pos * scale).FloorToInt(); Rfloat2 t = pos * scale - rint; rint += size - Rint2.One; int x = rint.x; int y = rint.y; if (x < 0 || x >= 2 * size.x - 2) { return defaultValue; } if (y < 0 || y >= 2 * size.y - 2) { return defaultValue; } Rfloat4X4 f = new Rfloat4X4(data[x, y], data[x + 1, y], data[x + 2, y], data[x + 3, y], data[x, y + 1], data[x + 1, y + 1], data[x + 2, y + 1], data[x + 3, y + 1], data[x, y + 2], data[x + 1, y + 2], data[x + 2, y + 2], data[x + 3, y + 2], data[x, y + 3], data[x + 1, y + 3], data[x + 2, y + 3], data[x + 3, y + 3]); (double, Rfloat2) tuple = InterpolationTools.BicubicWithGradient(t, in f, in matrix); double item = tuple.Item1; Rfloat2 item2 = tuple.Item2; gradient = item2 * scale; return item; } } public class HermiteInterpolator { private struct InertSample { public InertFloat value; public InertFloat gradx; public InertFloat grady; public InertFloat mixed; public static implicit operator InterpolationTools.PointSample(InertSample sample) { return new InterpolationTools.PointSample(sample.value, sample.gradx, sample.grady, sample.mixed); } } private readonly Rint2 size; private readonly Rfloat2 scale; private readonly InertSample[,] data; public HermiteInterpolator(Rfloat2 range, Rfloat2 resolution, Func signal) { HermiteInterpolator hermiteInterpolator = this; size = (range / resolution).CeilToInt() + Rint2.One; scale = new Rfloat2(1.0 / resolution.x, 1.0 / resolution.y); data = new InertSample[2 * size.x + 1, 2 * size.y + 1]; Parallel.For(0, data.GetLength(1), delegate(int j) { for (int k = 0; k < hermiteInterpolator.data.GetLength(0); k++) { hermiteInterpolator.data[k, j].value = signal(resolution * (new Rfloat2(k, j) - hermiteInterpolator.size)); } }); Parallel.For(1, data.GetLength(1) - 1, delegate(int j) { for (int i = 1; i < hermiteInterpolator.data.GetLength(0) - 1; i++) { hermiteInterpolator.data[i, j].gradx = ((double)hermiteInterpolator.data[i + 1, j].value - (double)hermiteInterpolator.data[i - 1, j].value) / 2.0; hermiteInterpolator.data[i, j].grady = ((double)hermiteInterpolator.data[i, j + 1].value - (double)hermiteInterpolator.data[i, j - 1].value) / 2.0; hermiteInterpolator.data[i, j].mixed = ((double)hermiteInterpolator.data[i + 1, j + 1].value - (double)hermiteInterpolator.data[i + 1, j - 1].value - (double)hermiteInterpolator.data[i - 1, j + 1].value + (double)hermiteInterpolator.data[i - 1, j - 1].value) / 4.0; } }); } public double Interpolate(Rfloat2 pos, double defaultValue = 0.0) { Rint2 rint = (pos * scale).FloorToInt(); Rfloat2 t = pos * scale - rint; rint += size; int x = rint.x; int y = rint.y; if (x < 1 || x >= 2 * size.x - 1) { return defaultValue; } if (y < 1 || y >= 2 * size.y - 1) { return defaultValue; } Rfloat4X4 f = InterpolationTools.BuildHermiteMatrix(data[x, y], data[x, y + 1], data[x + 1, y], data[x + 1, y + 1]); return InterpolationTools.Bicubic(t, in f, in InterpolationTools.MatrixHermite); } } public class DistanceWeightedInterpolator { [Serializable] public struct Config { public enum Truncation { None, Linear, Quadratic, SmoothStep } public Truncation truncation; } public struct Kernel { public bool singular; public double reach; public Func func; } private readonly Kernel kernel; private readonly Config config; private readonly Tiling tiling; private readonly TileData values; private readonly TileData neighborMap; public DistanceWeightedInterpolator(Kernel kernel, Config config, Tiling tiling, TileData values) { this.kernel = kernel; this.config = config; this.tiling = tiling; this.values = values; neighborMap = tiling.BuildScanMap(kernel.reach + tiling.Spacing); } public double Interpolate(Rfloat2 pos, double defaultValue = 0.0) { Tile? tile = tiling.GetTile(pos); if (tile.HasValue) { Tile valueOrDefault = tile.GetValueOrDefault(); if (kernel.singular && valueOrDefault.Center == pos) { return values[valueOrDefault]; } double num = kernel.reach * kernel.reach; double num2 = Weight((valueOrDefault.Center - pos).SqrLength, num); double num3 = (double)valueOrDefault.Data(values) * num2; int[] array = neighborMap[valueOrDefault]; foreach (int id in array) { Tile tile2 = tiling.GetTile(id); double sqrLength = (tile2.Center - pos).SqrLength; if (!(sqrLength > num)) { double num4 = Weight(sqrLength, num); num2 += num4; num3 += (double)tile2.Data(values) * num4; } } return num3 / num2; } return defaultValue; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private double Weight(double d2, double r2) { double num = kernel.func(d2); return num * config.truncation switch { Config.Truncation.None => 1.0, Config.Truncation.Linear => 1.0 - Rmath.Sqrt(d2 / r2), Config.Truncation.Quadratic => 1.0 - d2 / r2, Config.Truncation.SmoothStep => Rmath.Smooth3(1.0 - d2 / r2), _ => throw new ArgumentOutOfRangeException(), }; } } public static class InterpolationTools { public readonly struct PointSample { public readonly double value; public readonly double gradx; public readonly double grady; public readonly double mixed; public PointSample(double value, double gradx, double grady, double mixed) { this.value = value; this.gradx = gradx; this.grady = grady; this.mixed = mixed; } } public static readonly Rfloat4X4 MatrixCatmullRom = new Rfloat4X4(0.0, 1.0, 0.0, 0.0, -0.5, 0.0, 0.5, 0.0, 1.0, -2.5, 2.0, -0.5, -0.5, 1.5, -1.5, 0.5); public static readonly Rfloat4X4 MatrixBSpline = new Rfloat4X4(1.0, 4.0, 1.0, 0.0, -3.0, 0.0, 3.0, 0.0, 3.0, -6.0, 3.0, 0.0, -1.0, 3.0, -3.0, 1.0) / 6.0; public static readonly Rfloat4X4 MatrixHermite = new Rfloat4X4(1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -3.0, 3.0, -2.0, -1.0, 2.0, -2.0, 1.0, 1.0); [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Bicubic(Rfloat2 t, in Rfloat4X4 f, in Rfloat4X4 m) { Rfloat4 rfloat = new Rfloat4(1.0, t.x, t.x * t.x, t.x * t.x * t.x); Rfloat4 rfloat2 = new Rfloat4(1.0, t.y, t.y * t.y, t.y * t.y * t.y); Rfloat4 lhs = rfloat * m; Rfloat4 rfloat3 = rfloat2 * m; return Rfloat4.Dot(lhs, rfloat3 * f); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static (double, Rfloat2) BicubicWithGradient(Rfloat2 t, in Rfloat4X4 f, in Rfloat4X4 m) { Rfloat4 rfloat = new Rfloat4(1.0, t.x, t.x * t.x, t.x * t.x * t.x); Rfloat4 rfloat2 = new Rfloat4(1.0, t.y, t.y * t.y, t.y * t.y * t.y); Rfloat4 rfloat3 = new Rfloat4(0.0, 1.0, 2.0 * t.x, 3.0 * t.x * t.x); Rfloat4 rfloat4 = new Rfloat4(0.0, 1.0, 2.0 * t.y, 3.0 * t.y * t.y); Rfloat4 lhs = rfloat * m; Rfloat4 rfloat5 = rfloat2 * m; Rfloat4 lhs2 = rfloat3 * m; Rfloat4 rfloat6 = rfloat4 * m; Rfloat4 rhs = rfloat5 * f; Rfloat4 rhs2 = rfloat6 * f; return (Rfloat4.Dot(lhs, rhs), new Rfloat2(Rfloat4.Dot(lhs2, rhs), Rfloat4.Dot(lhs, rhs2))); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat4X4 BuildHermiteMatrix(PointSample p00, PointSample p01, PointSample p10, PointSample p11) { return new Rfloat4X4(p00.value, p10.value, p00.gradx, p10.gradx, p01.value, p11.value, p01.gradx, p11.gradx, p00.grady, p10.grady, p00.mixed, p10.mixed, p01.grady, p11.grady, p01.mixed, p11.mixed); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Rfloat4X4 BuildHermiteMatrix(Rfloat2 g00, Rfloat2 g01, Rfloat2 g10, Rfloat2 g11) { return new Rfloat4X4(0.0, 0.0, g00.x, g10.x, 0.0, 0.0, g01.x, g11.x, g00.y, g10.y, 0.0, 0.0, g01.y, g11.y, 0.0, 0.0); } } public static class RadialKernels { [Serializable] public struct InverseDistanceConfig { public double power; public double reach; public double epsilon; } [Serializable] public struct GaussianConfig { public double sigma; public double reach; } public static DistanceWeightedInterpolator.Kernel InverseDistance(InverseDistanceConfig config, double tileSpacing) { double p = 0.5 * config.power; double e = config.epsilon * tileSpacing; DistanceWeightedInterpolator.Kernel result = default(DistanceWeightedInterpolator.Kernel); result.singular = true; result.reach = config.reach * tileSpacing; result.func = (double r2) => 1.0 / (Rmath.Pow(r2, p) + e); return result; } public static DistanceWeightedInterpolator.Kernel Gaussian(GaussianConfig config, double tileSpacing) { double num = tileSpacing * config.sigma; double i = -1.0 / (2.0 * num * num); DistanceWeightedInterpolator.Kernel result = default(DistanceWeightedInterpolator.Kernel); result.singular = false; result.reach = num * config.reach; result.func = (double r2) => Rmath.Exp(i * r2); return result; } } public static class SmoothMin { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Quadratic(double a, double b, double k) { double num = k * 4.0; double num2 = Rmath.Max(0.0, num - Rmath.Abs(a - b)) / num; return Rmath.Min(a, b) - num2 * num2 * k; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Cubic(double a, double b, double k) { double num = k * 6.0; double num2 = Rmath.Max(0.0, num - Rmath.Abs(a - b)) / num; return Rmath.Min(a, b) - num2 * num2 * num2 * k; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Cubic(double a, double b, double k, out double t) { double num = k * 6.0; double num2 = Rmath.Max(0.0, num - Rmath.Abs(a - b)) / num; double num3 = num2 * num2; t = ((a < b) ? (num3 * 0.5) : (1.0 - num3 * 0.5)); return Rmath.Min(a, b) - num3 * num2 * k; } } public static class SmoothMax { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Quadratic(double a, double b, double k) { double num = k * 4.0; double num2 = Rmath.Max(0.0, num - Rmath.Abs(a - b)) / num; return Rmath.Max(a, b) + num2 * num2 * k; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Cubic(double a, double b, double k) { double num = k * 6.0; double num2 = Rmath.Max(0.0, num - Rmath.Abs(a - b)) / num; return Rmath.Max(a, b) + num2 * num2 * num2 * k; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static double Cubic(double a, double b, double k, out double t) { double num = k * 6.0; double num2 = Rmath.Max(0.0, num - Rmath.Abs(a - b)) / num; double num3 = num2 * num2; t = ((a > b) ? (num2 * 0.5) : (1.0 - num2 * 0.5)); return Rmath.Max(a, b) + num3 * num2 * k; } } } namespace Riverheim.Util.Grids { public class RectangularGrid { public readonly struct Cell { public Rint2 Pos { get; } public RectangularGrid Grid { get; } public Rfloat2 Center => Grid.GetCellCenter(Pos); public ref T Data => ref Grid.storage[Pos.x, Pos.y]; public int HopsToBounds => Grid.HopsToBounds(Pos); public Cell(Rint2 pos, RectangularGrid grid) { Pos = pos; Grid = grid; } public ref V SisterData(RectangularGrid grid) { return ref grid.storage[Pos.x, Pos.y]; } public IEnumerable GetNeighbors() { return GetNeighbors(1); } public IEnumerable GetNeighbors(int hops) { return Grid.GetNeighbors(Pos, hops); } } private class TilingSpatialIndex : ITilingSpatialIndex { private readonly Rint2 size; private readonly Rfloat2 cellSize; private readonly Rfloat2 offset; public TilingSpatialIndex(Rint2 size, Rfloat2 cellSize, Rfloat2 offset) { this.size = size; this.cellSize = cellSize; this.offset = offset; } public int GetTileId(Rfloat2 pos) { Rint2 pos2 = ((pos + offset) / cellSize).FloorToInt(); if (OutOfBounds(pos2)) { return -1; } return pos2.x + pos2.y * size.x; } private bool OutOfBounds(Rint2 pos) { if (pos.x >= 0 && pos.y >= 0 && pos.x < size.x) { return pos.y >= size.y; } return true; } } [CompilerGenerated] private sealed class d__20 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private Cell <>2__current; private int <>l__initialThreadId; public RectangularGrid <>4__this; private int 5__2; private int 5__3; Cell IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__20(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; RectangularGrid rectangularGrid = <>4__this; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; 5__3++; goto IL_006d; } <>1__state = -1; 5__2 = 0; goto IL_0090; IL_006d: if (5__3 < rectangularGrid.Size.y) { <>2__current = new Cell(new Rint2(5__2, 5__3), rectangularGrid); <>1__state = 1; return true; } 5__2++; goto IL_0090; IL_0090: if (5__2 < rectangularGrid.Size.x) { 5__3 = 0; goto IL_006d; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__20 result; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; result = this; } else { result = new d__20(0) { <>4__this = <>4__this }; } return result; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [CompilerGenerated] private sealed class d__23 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private Cell <>2__current; private int <>l__initialThreadId; private Rint2 pos; public Rint2 <>3__pos; private int hops; public int <>3__hops; public RectangularGrid <>4__this; private int 5__2; private int 5__3; Cell IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__23(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; RectangularGrid rectangularGrid = <>4__this; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_009f; } <>1__state = -1; 5__2 = pos.y - hops; goto IL_00d9; IL_00d9: if (5__2 <= pos.y + hops) { 5__3 = pos.x - hops; goto IL_00af; } return false; IL_00af: if (5__3 <= pos.x + hops) { Rint2 rint = new Rint2(5__3, 5__2); if (!(rint == pos) && !rectangularGrid.OutOfBounds(rint)) { <>2__current = new Cell(rint, rectangularGrid); <>1__state = 1; return true; } goto IL_009f; } 5__2++; goto IL_00d9; IL_009f: 5__3++; goto IL_00af; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__23 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__23(0) { <>4__this = <>4__this }; } d__.pos = <>3__pos; d__.hops = <>3__hops; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } private readonly T[,] storage; public Rint2 Size { get; } public Rfloat2 CellSize { get; } public Rfloat2 Offset { get; } public int Count => Size.x * Size.y; public double Spacing => (CellSize.x + CellSize.y) / 2.0; public RectangularGrid(Rint2 size, Rfloat2 cellSize, Rfloat2 offset) { Size = size; CellSize = cellSize; Offset = offset; storage = new T[size.x, size.y]; } public RectangularGrid(Rint2 size, Rfloat2 cellSize) : this(size, cellSize, Rfloat2.Zero) { } public RectangularGrid CreateSisterGrid(Func tileConv = null) { RectangularGrid rectangularGrid = new RectangularGrid(Size, CellSize, Offset); if (tileConv != null) { foreach (Cell item in All()) { rectangularGrid.storage[item.Pos.x, item.Pos.y] = tileConv(item); } } return rectangularGrid; } public Cell? GetCell(Rint2 pos) { if (!OutOfBounds(pos)) { return new Cell(pos, this); } return null; } public Cell? GetCell(Rfloat2 pos) { return GetCell(GetGridPos(pos)); } [IteratorStateMachine(typeof(RectangularGrid<>.d__20))] public IEnumerable All() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__20(-2) { <>4__this = this }; } private Rfloat2 GetCellCenter(Rint2 pos) { return (pos + Rfloat2.One / 2.0) * CellSize - Offset; } private int HopsToBounds(Rint2 pos) { return Rmath.Min(Rmath.Min(pos.x, Size.x - pos.x - 1), Rmath.Min(pos.y, Size.y - pos.y - 1)); } [IteratorStateMachine(typeof(RectangularGrid<>.d__23))] private IEnumerable GetNeighbors(Rint2 pos, int hops) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__23(-2) { <>4__this = this, <>3__pos = pos, <>3__hops = hops }; } private Rint2 GetGridPos(Rfloat2 pos) { return ((pos + Offset) / CellSize).FloorToInt(); } private bool OutOfBounds(Rint2 pos) { if (pos.x >= 0 && pos.y >= 0 && pos.x < Size.x) { return pos.y >= Size.y; } return true; } private double GetCellArea() { return CellSize.x * CellSize.y; } private Rint2 GetCellPos(int id) { return new Rint2(id % Size.x, id / Size.x); } private int GetTileId(Rint2 pos) { return pos.x + pos.y * Size.x; } public Tiling ToTiling() { List list = new List(); for (int i = 0; i < Count; i++) { Rint2 cellPos = GetCellPos(i); list.Add(new Tiling.TileInfo { center = GetCellCenter(cellPos), area = GetCellArea(), hopsToBounds = HopsToBounds(cellPos), adjacency = (from neighbor in GetNeighbors(cellPos, 1) select GetTileId(neighbor.Pos)).ToArray() }); } return new Tiling(Spacing, list, new TilingSpatialIndex(Size, CellSize, Offset)); } } public class SquareGrid : RectangularGrid { public new double CellSize => base.CellSize.x; public SquareGrid(Rint2 size, double cellSize, Rfloat2 offset) : base(size, new Rfloat2(cellSize, cellSize), offset) { } public SquareGrid(Rint2 size, double cellSize) : this(size, cellSize, Rfloat2.Zero) { } } } namespace Riverheim.Util.Geometry { public readonly struct BarycentricCoordinates { public readonly double u; public readonly double v; public readonly double w; public double this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return index switch { 0 => u, 1 => v, 2 => w, _ => throw new IndexOutOfRangeException("Invalid Barycentric Coordinates index!"), }; } } public bool IsInterior { get { if (u >= 0.0 && v >= 0.0) { return w >= 0.0; } return false; } } public BarycentricCoordinates(double v, double w) { u = 1.0 - v - w; this.v = v; this.w = w; } } public struct BoundingBox { public double x; public double y; public double w; public double h; public Rfloat2 Pos { get { return new Rfloat2(x, y); } set { double num = value.x; double num2 = value.y; x = num; y = num2; } } public Rfloat2 Size { get { return new Rfloat2(w, h); } set { double num = value.x; double num2 = value.y; w = num; h = num2; } } public Rfloat2 Center { get { return new Rfloat2(x + w / 2.0, y + h / 2.0); } set { x = value.x - w / 2.0; y = value.y - h / 2.0; } } public BoundingBox(Rfloat2 pos, Rfloat2 size) { x = pos.x; y = pos.y; w = size.x; h = size.y; } public BoundingBox(IEnumerable points) { double num = double.MaxValue; double num2 = double.MaxValue; double num3 = double.MinValue; double num4 = double.MinValue; foreach (Rfloat2 point in points) { num = Rmath.Min(point.x, num); num2 = Rmath.Min(point.y, num2); num3 = Rmath.Max(point.x, num3); num4 = Rmath.Max(point.y, num4); } x = num; y = num2; w = num3 - num; h = num4 - num2; } public static BoundingBox ZeroCentered(Rfloat2 size) { return new BoundingBox(-0.5 * size, size); } public readonly bool Contains(Rfloat2 point) { if (point.x >= x && point.x <= x + w && point.y >= y) { return point.y <= y + h; } return false; } public readonly bool Overlaps(BoundingBox other) { if (other.x + other.w > x && other.x < x + w && other.y + other.h > y) { return other.y < y + h; } return false; } public BoundingBox Translate(Rfloat2 offset) { return new BoundingBox(Pos + offset, Size); } public BoundingBox Expand(double amount) { return new BoundingBox(Pos - amount, Size + 2.0 * amount); } public override string ToString() { return $"BoundingBox({Pos}, {Size})"; } } public static class DistanceFunctions { public delegate double Sdf(Rfloat2 pos); public static Sdf MergeSdf(double smoothing, params Sdf[] sdfs) { return (Rfloat2 pos) => sdfs.Aggregate(double.MaxValue, (double current, Sdf sdf) => SmoothMin.Cubic(current, sdf(pos), smoothing)); } public static Sdf SegmentSdf(Rfloat2 a, Rfloat2 b, out BoundingBox contour) { contour = new BoundingBox(new Rfloat2[2] { a, b }); return (Rfloat2 pos) => Segment(pos, a, b); } public static Sdf CircleSdf(Rfloat2 center, double radius, out BoundingBox contour) { contour = new BoundingBox(center - radius, 2.0 * radius * Rfloat2.One); return (Rfloat2 pos) => Rmath.Abs((pos - center).Length - radius); } public static Sdf DiskSdf(Rfloat2 center, double radius, out BoundingBox contour) { contour = new BoundingBox(center - radius, 2.0 * radius * Rfloat2.One); return (Rfloat2 pos) => (pos - center).Length - radius; } public static Sdf PointSdf(Rfloat2 p, out BoundingBox contour) { contour = new BoundingBox(p, Rfloat2.Zero); return (Rfloat2 pos) => (pos - p).Length; } public static Sdf UnevenCapsuleSdf(Rfloat2 a, Rfloat2 b, double ra, double rb, out BoundingBox contour) { contour = new BoundingBox(new Rfloat2[2] { a, b }).Expand(Rmath.Max(ra, rb)); return (Rfloat2 pos) => UnevenCapsule(pos, a, b, ra, rb); } public static double Segment(Rfloat2 pos, Rfloat2 a, Rfloat2 b) { Rfloat2 rfloat = pos - a; Rfloat2 rfloat2 = b - a; if (rfloat2.Equals(Rfloat2.Zero)) { return rfloat.Length; } double num = Rmath.Clamp01(Rfloat2.Dot(rfloat, rfloat2) / Rfloat2.Dot(rfloat2, rfloat2)); return (rfloat - num * rfloat2).Length; } public static double SegmentSquared(Rfloat2 pos, Rfloat2 a, Rfloat2 b, out double h) { h = 0.0; Rfloat2 rfloat = pos - a; Rfloat2 rfloat2 = b - a; if (rfloat2.Equals(Rfloat2.Zero)) { return rfloat.SqrLength; } h = Rmath.Clamp01(Rfloat2.Dot(rfloat, rfloat2) / Rfloat2.Dot(rfloat2, rfloat2)); return (rfloat - h * rfloat2).SqrLength; } public static double UnevenCapsule(Rfloat2 pos, Rfloat2 a, Rfloat2 b, double ra, double rb) { pos -= a; b -= a; double sqrLength = b.SqrLength; Rfloat2 rfloat = new Rfloat2(Rmath.Abs(Rfloat2.Dot(pos, new Rfloat2(b.y, 0.0 - b.x))), Rfloat2.Dot(pos, b)) / sqrLength; double num = ra - rb; Rfloat2 lhs = new Rfloat2(Rmath.Sqrt(sqrLength - num * num), num); double num2 = Rfloat2.Cross(lhs, rfloat); double num3 = Rfloat2.Dot(lhs, rfloat); double num4 = Rfloat2.Dot(rfloat, rfloat); if (num2 < 0.0) { return Rmath.Sqrt(sqrLength * num4) - ra; } if (num2 > lhs.x) { return Rmath.Sqrt(sqrLength * (num4 + 1.0 - 2.0 * rfloat.y)) - rb; } return num3 - ra; } } public class FrozenSpatialGrid { public class Builder { private readonly double cellSize; private readonly Rfloat2 offset; private readonly Rint2 size; private readonly List[] data; public Builder(double cellSize, Rfloat2 offset, Rfloat2 size) { this.cellSize = cellSize; this.offset = offset; this.size = (size / cellSize).CeilToInt(); data = new List[this.size.x * this.size.y]; for (int i = 0; i < data.Length; i++) { data[i] = new List(); } } public void Add(T geometry, BoundingBox bbox, Predicate bboxIntersection = null) { Rfloat2 rfloat = bbox.Pos - offset; for (int i = Index(rfloat.x); i <= Index(rfloat.x + bbox.w); i++) { for (int j = Index(rfloat.y); j <= Index(rfloat.y + bbox.h); j++) { TryAdd(new Rint2(i, j), geometry, bboxIntersection ?? new Predicate(((BoundingBox)bbox).Overlaps)); } } } private void TryAdd(Rint2 index, T geometry, Predicate bboxIntersection) { if (index.x >= 0 && index.y >= 0 && index.x < size.x && index.y < size.y && bboxIntersection(CellRect(index))) { data[index.x * size.y + index.y].Add(geometry); } } private int Index(double x) { return Rmath.FloorToInt(x / cellSize); } private BoundingBox CellRect(Rint2 index) { return new BoundingBox(cellSize * (Rfloat2)index + offset, cellSize * Rfloat2.One); } public FrozenSpatialGrid Build() { T[][] array = new T[size.x * size.y][]; for (int i = 0; i < data.Length; i++) { array[i] = data[i].ToArray(); } return new FrozenSpatialGrid(cellSize, offset, size, array); } } private readonly double cellSize; private readonly Rfloat2 offset; private readonly Rint2 size; private readonly T[][] data; private FrozenSpatialGrid(double cellSize, Rfloat2 offset, Rint2 size, T[][] data) { this.cellSize = cellSize; this.offset = offset; this.size = size; this.data = data; } public T[] ApproximateQuery(Rfloat2 pos) { pos -= offset; int num = Index(pos.x); int num2 = Index(pos.y); if (num < 0 || num >= size.x || num2 < 0 || num2 >= size.y) { return Array.Empty(); } return data[num * size.y + num2]; } private int Index(double x) { return Rmath.FloorToInt(x / cellSize); } } public class FrozenSpatialHashGrid { public class Builder { private readonly double cellSize; private readonly Dictionary> data; public Builder(double cellSize) { this.cellSize = cellSize; data = new Dictionary>(); } public void Add(T geometry, BoundingBox bbox, Predicate bboxIntersection = null) { for (int i = Index(bbox.x); i <= Index(bbox.x + bbox.w); i++) { for (int j = Index(bbox.y); j <= Index(bbox.y + bbox.h); j++) { TryAdd(new Rint2(i, j), geometry, bboxIntersection ?? new Predicate(((BoundingBox)bbox).Overlaps)); } } } private void TryAdd(Rint2 index, T geometry, Predicate bboxIntersection) { if (bboxIntersection(CellRect(index))) { if (!data.ContainsKey(index)) { data[index] = new List(); } data[index].Add(geometry); } } private int Index(double x) { return Rmath.FloorToInt(x / cellSize); } private BoundingBox CellRect(Rint2 index) { return new BoundingBox(cellSize * (Rfloat2)index, cellSize * Rfloat2.One); } public FrozenSpatialHashGrid Build() { Dictionary dictionary = new Dictionary(); foreach (Rint2 key in data.Keys) { dictionary[key] = data[key].ToArray(); } return new FrozenSpatialHashGrid(cellSize, new ReadOnlyDictionary(dictionary)); } } private readonly double cellSize; private readonly ReadOnlyDictionary data; private FrozenSpatialHashGrid(double cellSize, ReadOnlyDictionary data) { this.cellSize = cellSize; this.data = data; } public T[] ApproximateQuery(Rfloat2 pos) { if (!data.TryGetValue((pos / cellSize).FloorToInt(), out var value)) { return Array.Empty(); } return value; } } public struct Segment { public Rfloat2 a; public Rfloat2 b; public double Length => Rfloat2.Distance(a, b); } public class Polyline { [CompilerGenerated] private sealed class d__5 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private Segment <>2__current; private int <>l__initialThreadId; public Polyline <>4__this; private int 5__2; Segment IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__5(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; Polyline polyline = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; 5__2 = 0; break; case 1: { <>1__state = -1; int num2 = 5__2 + 1; 5__2 = num2; break; } } if (5__2 < polyline.vertices.Length - 1) { <>2__current = new Segment { a = polyline.vertices[5__2], b = polyline.vertices[5__2 + 1] }; <>1__state = 1; return true; } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__5 result; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; result = this; } else { result = new d__5(0) { <>4__this = <>4__this }; } return result; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } private readonly InertFloat2[] vertices; public IEnumerable Vertices => ((IEnumerable)vertices).Select((Func)((InertFloat2 t) => t)); public IEnumerable Segments { [IteratorStateMachine(typeof(d__5))] get { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__5(-2) { <>4__this = this }; } } public double Length { get; } public Polyline(IEnumerable vertices) { this.vertices = vertices.Select((Func)((Rfloat2 vertex) => vertex)).ToArray(); Length = Segments.Sum((Segment segment) => segment.Length); } } public class TriangleMesh { private class TriangleWrapper { private struct Cache { public Rfloat2 a; public double s1; public double s2; public double s3; public double invDenom; } public readonly int index; private readonly Cache cache; public TriangleWrapper(int index, Rfloat2 a, Rfloat2 b, Rfloat2 c) { this.index = index; cache = GetBaryCache(a, b, c); } private static Cache GetBaryCache(Rfloat2 a, Rfloat2 b, Rfloat2 c) { double num = c.y - a.y; double num2 = c.x - a.x; double num3 = b.y - a.y; Cache result = default(Cache); result.a = a; result.s1 = num; result.s2 = num2; result.s3 = num3; result.invDenom = 1.0 / (num3 * num2 - (b.x - a.x) * num); return result; } public BarycentricCoordinates GetBc(Rfloat2 point) { double num = point.y - cache.a.y; double num2 = (cache.a.x * cache.s1 + num * cache.s2 - point.x * cache.s1) * cache.invDenom; double w = (num - num2 * cache.s3) / cache.s1; return new BarycentricCoordinates(num2, w); } } [CompilerGenerated] private sealed class d__6 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private int <>2__current; private int <>l__initialThreadId; private int triangleIndex; public int <>3__triangleIndex; public TriangleMesh <>4__this; private int 5__2; int IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__6(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; TriangleMesh triangleMesh = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; 5__2 = triangleIndex * 3; <>2__current = triangleMesh.triangles[5__2]; <>1__state = 1; return true; case 1: <>1__state = -1; <>2__current = triangleMesh.triangles[5__2 + 1]; <>1__state = 2; return true; case 2: <>1__state = -1; <>2__current = triangleMesh.triangles[5__2 + 2]; <>1__state = 3; return true; case 3: <>1__state = -1; return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__6 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__6(0) { <>4__this = <>4__this }; } d__.triangleIndex = <>3__triangleIndex; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } private readonly List triangles; private readonly FrozenSpatialGrid spatialIndex; public TriangleMesh(List points, List triangles, BoundingBox bounds, double spacingHint) { this.triangles = triangles; if (triangles.Count % 3 != 0) { throw new ArgumentException("Triangle count must be a multiple of 3"); } for (int i = 0; i < triangles.Count; i += 3) { int num = triangles[i]; int num2 = triangles[i + 1]; int num3 = triangles[i + 2]; if (num == num2 || num2 == num3 || num3 == num) { throw new ArgumentException($"Triangle #{i} is degenerate"); } if (num < 0 || num2 < 0 || num3 < 0) { throw new ArgumentException($"Triangle #{i} has negative vertex indices"); } if (num >= points.Count || num2 >= points.Count || num3 >= points.Count) { throw new ArgumentException($"Triangle #{i} has invalid vertex indices"); } } FrozenSpatialGrid.Builder builder = new FrozenSpatialGrid.Builder(spacingHint, bounds.Pos, bounds.Size); for (int j = 0; j < triangles.Count; j += 3) { Rfloat2 rfloat = points[triangles[j]]; Rfloat2 rfloat2 = points[triangles[j + 1]]; Rfloat2 rfloat3 = points[triangles[j + 2]]; BoundingBox bbox = new BoundingBox(new List { rfloat, rfloat2, rfloat3 }); TriangleWrapper geometry = new TriangleWrapper(j / 3, rfloat, rfloat2, rfloat3); builder.Add(geometry, bbox); } spatialIndex = builder.Build(); } public int GetTriangleIndex(Rfloat2 pos, out BarycentricCoordinates bc) { bc = default(BarycentricCoordinates); TriangleWrapper[] array = spatialIndex.ApproximateQuery(pos); if (array.Length == 0) { return -1; } TriangleWrapper[] array2 = array; foreach (TriangleWrapper triangleWrapper in array2) { bc = triangleWrapper.GetBc(pos); if (bc.IsInterior) { return triangleWrapper.index; } } return -1; } public int GetClosestVertex(int triangleIndex, BarycentricCoordinates bc) { int num = triangleIndex * 3; int num2 = ((!(bc.v > bc.u)) ? ((bc.w > bc.u) ? 2 : 0) : ((!(bc.w > bc.v)) ? 1 : 2)); return triangles[num + num2]; } public int GetVertex(int triangleIndex, int vertexIndex) { return triangles[triangleIndex * 3 + vertexIndex]; } [IteratorStateMachine(typeof(d__6))] public IEnumerable GetVertices(int triangleIndex) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__6(-2) { <>4__this = this, <>3__triangleIndex = triangleIndex }; } public bool GetSortedVertices(Rfloat2 pos, ref Span<(int, double)> result) { BarycentricCoordinates bc; int triangleIndex = GetTriangleIndex(pos, out bc); if (triangleIndex == -1) { return false; } int num = triangleIndex * 3; bool num2 = bc.u > bc.v; bool flag = bc.v > bc.w; bool flag2 = bc.w > bc.u; int num3; int num4; int num5; if (!num2) { if (!flag2) { num3 = 1; num4 = 0; num5 = 2; } else if (!flag) { num3 = 2; num4 = 1; num5 = 0; } else { num3 = 1; num4 = 2; num5 = 0; } } else if (!flag) { if (!flag2) { num3 = 0; num4 = 2; num5 = 1; } else { num3 = 2; num4 = 0; num5 = 1; } } else { num3 = 0; num4 = 1; num5 = 2; } result[0] = (triangles[num + num3], bc[num3]); result[1] = (triangles[num + num4], bc[num4]); result[2] = (triangles[num + num5], bc[num5]); return true; } } } namespace Riverheim.Util.Debug { public static class Dump { public static string ToString(object obj, int indent = 0) { if (obj == null) { return "null"; } Type type = obj.GetType(); if (type == typeof(string)) { return "\"" + (string)obj + "\""; } if (type.IsPrimitive || type.IsSubclassOf(typeof(Type))) { return obj.ToString(); } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(type.Name).Append(" {\n"); Type[] interfaces = type.GetInterfaces(); foreach (Type type2 in interfaces) { if (type2 == typeof(IDictionary)) { IDictionary dictionary = (IDictionary)obj; foreach (object key in dictionary.Keys) { stringBuilder.Append(new string(' ', indent + 2)).Append("[" + ToString(key, indent + 4) + " : " + ToString(dictionary[key], indent + 4) + "],\n"); } return stringBuilder.Append(new string(' ', indent)).Append("}").ToString(); } if (!(type2 == typeof(IEnumerable))) { continue; } foreach (object item in (IEnumerable)obj) { stringBuilder.Append(new string(' ', indent + 2)).Append(ToString(item, indent + 4) + ",\n"); } return stringBuilder.Append(new string(' ', indent)).Append("}").ToString(); } FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { stringBuilder.Append(new string(' ', indent + 2)).Append(fieldInfo.Name + " = " + ToString(fieldInfo.GetValue(obj), indent + 2) + ",\n"); } return stringBuilder.Append(new string(' ', indent)).Append("}").ToString(); } } public static class Logger { public enum LogLevel { Info, Warning, Error } private static Action impl = delegate(LogLevel _, object message) { Console.WriteLine(message); }; public static void SetLogger(Action logger) { impl = logger; } public static void LogInfo(object message) { impl(LogLevel.Info, message); } public static void LogWarning(object message) { impl(LogLevel.Warning, message); } public static void LogError(object message) { impl(LogLevel.Error, message); } } public class StopWatch : IDisposable { private readonly Action onCompleted; private readonly DateTime startTime; public StopWatch(Action onCompleted) { this.onCompleted = onCompleted; startTime = DateTime.Now; } public void Dispose() { onCompleted(DateTime.Now - startTime); } } } namespace Riverheim.Rendering { public class BiomeRenderer { [Serializable] public struct Config { public bool isEnabled; public CommonBiomeRenderer.QuadNoise.Config coarse; public CommonBiomeRenderer.Config ocean; public MeadowsRenderer.Config meadows; public CommonBiomeRenderer.Config marsh; public CommonBiomeRenderer.Config forest; public SwampRenderer.Config swamp; public CommonBiomeRenderer.Config mountain; public CommonBiomeRenderer.Config plains; public MistlandsRenderer.Config mistlands; public MistgladeRenderer.Config mistglade; public CommonBiomeRenderer.Config ashlands; public LavaplainRenderer.Config lavaplain; public CommonBiomeRenderer.Config deepNorth; } public struct RenderResult { public double fineDelta; public ClutterRenderer.Params clutter; public double lava; public double fertility; public double terracing; public static RenderResult operator *(RenderResult r, double m) { return m * r; } public static RenderResult operator *(double m, RenderResult r) { RenderResult result = default(RenderResult); result.fineDelta = m * r.fineDelta; result.clutter = m * r.clutter; result.lava = m * r.lava; result.fertility = m * r.fertility; result.terracing = m * r.terracing; return result; } public static RenderResult operator +(RenderResult lhs, RenderResult rhs) { RenderResult result = default(RenderResult); result.fineDelta = lhs.fineDelta + rhs.fineDelta; result.clutter = lhs.clutter + rhs.clutter; result.lava = lhs.lava + rhs.lava; result.fertility = lhs.fertility + rhs.fertility; result.terracing = lhs.terracing + rhs.terracing; return result; } } private readonly Config config; private readonly ISingleBiomeRenderer[] biomes; public BiomeRenderer(RandomDirectory random, Config config, Func forestationMap) { this.config = config; CommonBiomeRenderer.QuadNoise quadNoise = new CommonBiomeRenderer.QuadNoise(random.Sub("CoarseNoise"), config.coarse); biomes = new ISingleBiomeRenderer[Enum.GetValues(typeof(Biome)).Length]; biomes[0] = new CommonBiomeRenderer(default(CommonBiomeRenderer.Config), quadNoise); biomes[1] = new CommonBiomeRenderer(config.ocean, quadNoise); biomes[2] = new MeadowsRenderer(random.Sub("Meadows"), config.meadows, forestationMap, quadNoise); biomes[3] = new CommonBiomeRenderer(config.marsh, quadNoise); biomes[4] = new CommonBiomeRenderer(config.forest, quadNoise); biomes[5] = new SwampRenderer(random.Sub("Swamp"), config.swamp); biomes[6] = new CommonBiomeRenderer(config.mountain, quadNoise); biomes[7] = new CommonBiomeRenderer(config.plains, quadNoise); biomes[8] = new MistlandsRenderer(random.Sub("Mistlands"), config.mistlands); biomes[9] = new MistgladeRenderer(random.Sub("Mistglade"), config.mistglade); biomes[10] = new CommonBiomeRenderer(config.ashlands, quadNoise); biomes[11] = new LavaplainRenderer(random.Sub("Lavaplain"), config.lavaplain); biomes[12] = new CommonBiomeRenderer(config.deepNorth, quadNoise); } public double RenderCoarse(Biome biome, Rfloat2 pos, double height, double saturation, ref double meta) { if (config.isEnabled) { return biomes[(int)biome].RenderCoarse(pos, height, saturation, ref meta); } return 0.0; } public RenderResult RenderFine(Biome biome, Rfloat2 pos, double slope, double meta) { if (config.isEnabled) { return biomes[(int)biome].RenderFine(pos, slope, meta); } return default(RenderResult); } } public class CliffRenderer { [Serializable] public struct Config { public bool isEnabled; public double gradientMagnitude; public ProfileUtility.SmoothRamp.Config ramping; public TerracingConfig terracing; } [Serializable] public struct TerracingConfig { public double height; public double variability; public double varianceFrequency; public double slopeFalloff; } private readonly Config config; private readonly NoiseField3d volumetricNoise; public CliffRenderer(RandomDirectory random, Config config) { this.config = config; volumetricNoise = random.Noise3D("Volumetric", 1.0); } public double Render(Rfloat2 pos, double height, double slope, double profileNoise) { if (!config.isEnabled) { return 0.0; } double num = ProfileUtility.SmoothRamp.Profile(slope, in config.ramping); double num2 = SampleTerraced(pos, height, num); double num3 = config.gradientMagnitude * profileNoise * num; return num2 + num3; } private double SampleTerraced(Rfloat2 pos, double height, double slope) { double height2 = config.terracing.height; double num = config.terracing.varianceFrequency / height2; double num2 = height2 * volumetricNoise.Sample(new Rfloat3(num * pos.x, num * pos.y, 0.5 * num * height)); double num3 = (height + config.terracing.variability * num2) / height2; int num4 = Rmath.FloorToInt(num3); return Rmath.Lerp(1.0 - Rmath.Exp((0.0 - config.terracing.slopeFalloff) * slope), height, height2 * ((double)num4 + Rmath.Smooth5(num3 - (double)num4))) - height; } } public class ClutterRenderer { [Serializable] public struct Config { public bool isEnabled; public double envelopeScale; public NoiseField.FractalConfig envelopeFractal; public int octaves; public double featureSize; public double lacunarity; public double envelopeOctaveOffset; public double envelopeTransitionWindow; public double smoothingRange; public double ultrafineScale; public double ultrafineAmplitude; } [Serializable] public struct Params { public double amplitude; public double ubiquity; public double gain; public double clamping; public double offset; public double bias; public static Params operator *(Params p, double m) { return m * p; } public static Params operator *(double m, Params p) { Params result = default(Params); result.amplitude = m * p.amplitude; result.ubiquity = m * p.ubiquity; result.gain = m * p.gain; result.clamping = m * p.clamping; result.offset = m * p.offset; result.bias = m * p.bias; return result; } public static Params operator +(Params lhs, Params rhs) { Params result = default(Params); result.amplitude = lhs.amplitude + rhs.amplitude; result.ubiquity = lhs.ubiquity + rhs.ubiquity; result.gain = lhs.gain + rhs.gain; result.clamping = lhs.clamping + rhs.clamping; result.offset = lhs.offset + rhs.offset; result.bias = lhs.bias + rhs.bias; return result; } } private readonly Config config; private readonly NoiseField envelope; private readonly NoiseField[] octaves; private readonly NoiseField ultrafine; public ClutterRenderer(RandomDirectory random, Config config) { this.config = config; double num = config.featureSize; octaves = new NoiseField[config.octaves]; for (int i = 0; i < octaves.Length; i++) { octaves[i] = random.Noise2D($"Octaves/{i}", num); num /= config.lacunarity; } envelope = random.BiasedNoise2D("Envelope", config.envelopeScale).Fractal(config.envelopeFractal); ultrafine = config.ultrafineAmplitude * random.Noise2D("Ultrafine", config.ultrafineScale); } public double Clutter(Rfloat2 pos, Params parameters) { if (!config.isEnabled) { return 0.0; } return ultrafine.Sample(pos) + parameters.amplitude * Sample(pos, parameters.ubiquity, parameters.gain, parameters.clamping, parameters.offset, parameters.bias, config.envelopeTransitionWindow, config.envelopeOctaveOffset, config.smoothingRange); } private double Sample(Rfloat2 pos, double ubiquity, double gain, double clampingReduction, double clampingOffset, double bias, double envelopeTransitionWidth, double envelopeOctaveOffset, double smoothingRange) { double value = envelope.Sample(pos); double num = 1.0 - clampingReduction; double num2 = 1.0; double num3 = 1.0 - ubiquity; double num4 = 0.0; for (uint num5 = 0u; num5 < octaves.Length; num5++) { double num6 = num2 * Rmath.Smooth3(Rmath.InverseLerp(octaves[num5].Sample(pos), 0.0 - num + clampingOffset, num + clampingOffset)); num6 *= Rmath.Smooth3(Rmath.InverseLerp(value, new Rmath.Range { min = num3 - envelopeTransitionWidth, max = num3 + envelopeTransitionWidth })); num4 = SMax(num4, num6, smoothingRange) - smoothingRange; num2 *= gain; num3 += envelopeOctaveOffset; } return num4 - bias; } private static double SMax(double a, double b, double k) { if (!(k > 0.0)) { return Rmath.Max(a, b); } return SmoothMax.Cubic(a, b, k); } } public record HeightMap { public Rfloat2 range; public Rfloat2 resolution; public float[,] data; } [Producer(new string[] { "Rendering/HeightMap" })] public class HeightMapBuilder : IGeneratorStage { [Serializable] public struct Config { public enum Method { InverseDistanceWeighted, GaussianWeighted, Barycentric } public double resolution; public Method method; public DistanceWeightedInterpolator.Config distanceWeighted; public RadialKernels.InverseDistanceConfig inverseDistance; public RadialKernels.GaussianConfig gaussian; public double warpIntensity; } public const string OUTPUT = "Rendering/HeightMap"; [Random("Rendering")] public RandomDirectory random; [Config] public CommonConfig commonConfig; [Config] public Config config; [Config] public WorldRenderer.Config rendererConfig; [Resource("MainTiling")] public Tiling tiling; [Resource("MainInterpolationMesh")] public TriangleMesh interpolationMesh; [Resource("Heights/JoinedHeights")] public TileData input; public HeightMap Compute() { VectorNoiseField warp = random.WarpNoise("Warp", commonConfig.tileSpacing, rendererConfig.biomeWarp); Func signal; switch (config.method) { case Config.Method.Barycentric: { BarycentricInterpolator ip = new BarycentricInterpolator(tiling, interpolationMesh); signal = (Rfloat2 pos) => ip.Interpolate(pos + config.warpIntensity * warp.Sample(pos), 0.0, input, commonConfig.oceanDepth); break; } case Config.Method.InverseDistanceWeighted: { DistanceWeightedInterpolator ip2 = new DistanceWeightedInterpolator(RadialKernels.InverseDistance(config.inverseDistance, commonConfig.tileSpacing), config.distanceWeighted, tiling, input); signal = (Rfloat2 pos) => ip2.Interpolate(pos + config.warpIntensity * warp.Sample(pos), commonConfig.oceanDepth); break; } case Config.Method.GaussianWeighted: { DistanceWeightedInterpolator ip3 = new DistanceWeightedInterpolator(RadialKernels.Gaussian(config.gaussian, commonConfig.tileSpacing), config.distanceWeighted, tiling, input); signal = (Rfloat2 pos) => ip3.Interpolate(pos + config.warpIntensity * warp.Sample(pos), commonConfig.oceanDepth); break; } default: throw new ArgumentOutOfRangeException(); } Rfloat2 range = new Rfloat2(commonConfig.worldSize); Rfloat2 resolution = new Rfloat2(config.resolution); return new HeightMap { range = range, resolution = resolution, data = BicubicInterpolator.PrepareData(range, resolution, signal) }; } } [Serializable] [CompositeConfig] public struct RenderingConfig { public HeightMapBuilder.Config heightmap; public WorldRenderer.Config renderer; } [Serializable] public struct CragRenderConfig { public double profileVariability; public double terraceVariability; public ProfileUtility.Rational.Config profile; } [Serializable] public struct PondRenderConfig { public double blendRange; public double contourVarianceRange; } [Serializable] public struct SplatConfig { public bool isEnabled; public ProfileUtility.Rational.Config profile; public double smaxBlendRange; public double sminBlendRange; public VectorNoiseField.AbsoluteWarpConfig warp; public double noiseAmplitude; public double rangeOvershoot; public CragRenderConfig crags; public PondRenderConfig ponds; } public class SplatRenderer { private readonly SplatConfig config; private readonly double lowerBound; private readonly FrozenSpatialHashGrid spatialIndex; private readonly VectorNoiseField warpNoise; public SplatRenderer(RandomDirectory random, SplatConfig config, double spacingHint, double lowerBound, Splat[] splats) { this.config = config; this.lowerBound = lowerBound; warpNoise = random.WarpNoise("SplatWarp", config.warp); FrozenSpatialHashGrid.Builder builder = new FrozenSpatialHashGrid.Builder(spacingHint); foreach (Splat splat in splats) { AddSplat(builder, splat); } spatialIndex = builder.Build(); } private void AddSplat(FrozenSpatialHashGrid.Builder indexBuilder, Splat splat) { ISplat splat2; if (!(splat is Crag crag)) { if (!(splat is Plateau plateau)) { if (!(splat is Pond pond)) { throw new ArgumentOutOfRangeException("splat", splat, null); } splat2 = MakePondSplatObject(pond); } else { splat2 = MakePlateauSplatObject(plateau); } } else { splat2 = MakeCragSplatObject(crag); } ISplat splat3 = splat2; indexBuilder.Add(new SplatObject(splat3, splat.pos, splat.height), splat3.Bbox.Translate(splat.pos)); } private CragSplat MakeCragSplatObject(Crag crag) { CragSplat.Config config = default(CragSplat.Config); config.radius = crag.radius; config.terrace = crag.terrace; config.radiusVariability = this.config.crags.profileVariability; config.terraceVariability = this.config.crags.terraceVariability; config.cliffiness = crag.cliffiness; config.profile = new ProfileUtility.Rational.Config { slope = this.config.crags.profile.slope, scale = this.config.crags.profile.scale * (double)crag.radius }; CragSplat.Config obj = config; double targetHeight = lowerBound - (double)crag.height - this.config.rangeOvershoot; return new CragSplat(obj, targetHeight); } private CliffSplat MakePlateauSplatObject(Plateau plateau) { double num = Rmath.Min(this.config.noiseAmplitude, (double)plateau.radius / 2.0); CliffSplat.Config config = default(CliffSplat.Config); config.fixedFlatRange = (double)plateau.radius - num; config.variableFlatRange = num; config.skirt = (plateau.inverted ? this.config.profile.Inverted : this.config.profile); config.incline = plateau.incline; config.bulgeDirection = plateau.bulgeDirection; config.bulgeMagnitude = plateau.bulgeMagnitude; config.inverted = plateau.inverted; BoundingBox contour; return new CliffSplat(config, targetHeight: plateau.inverted ? (Rmath.Max(0.0, plateau.maxNeighborHeight - (double)plateau.height) + this.config.rangeOvershoot) : (lowerBound - (double)plateau.height - this.config.rangeOvershoot), sdf: DistanceFunctions.SegmentSdf(plateau.p1, plateau.p2, out contour), contour: contour); } private FlatSplat MakePondSplatObject(Pond pond) { FlatSplat.Config config = default(FlatSplat.Config); config.blendRange = this.config.ponds.blendRange; config.contourVarianceRange = this.config.ponds.contourVarianceRange; BoundingBox contour; return new FlatSplat(config, DistanceFunctions.DiskSdf(Rfloat2.Zero, pond.radius, out contour), contour); } public double BlendSplats(Rfloat2 pos, double height, double profileVariance, ref double slope, ref double cliffiness) { if (!config.isEnabled) { return height; } pos += warpNoise.Sample(pos); SplatObject[] array = spatialIndex.ApproximateQuery(pos); foreach (SplatObject splatObject in array) { double slope2; double t; double b = splatObject.Render(pos, profileVariance, out slope2, out t); height = splatObject.BlendMode switch { ISplat.SplatBlendMode.SMax => SmoothMax.Cubic(height, b, config.smaxBlendRange, out t), ISplat.SplatBlendMode.SMin => SmoothMin.Cubic(height, b, config.sminBlendRange, out t), ISplat.SplatBlendMode.Mix => Rmath.Lerp(t, height, b), _ => throw new ArgumentOutOfRangeException(), }; slope = Rmath.Lerp(t, slope, Rmath.Abs(slope2)); cliffiness = Rmath.Lerp(t, cliffiness, splatObject.Cliffiness); } return height; } } public class WorldRenderer : IWorldGen { [Serializable] public struct ShapingConfig { public double terracingStep; public double worldEdgeWidth; public double offworldDepth; } [Serializable] public struct ProfileNoiseConfig { public double featureSize; public NoiseField.FractalConfig fractal; public double bias; } [Serializable] public record Config { public BicubicInterpolator.Method interpolationMethod; public VectorNoiseField.RelativeWarpConfig biomeWarp; public RiverRenderer.Config rivers; public BiomeRenderer.Config biomes; public ShapingConfig shaping; public CliffRenderer.Config cliffs; public SplatConfig splats; public ProfileNoiseConfig profileNoise; public ClutterRenderer.Config clutter; } public const string RANDOM_KEY_WARP = "Warp"; private readonly CommonConfig commonConfig; private readonly Config config; private readonly WorldState state; private readonly VectorNoiseField biomeWarpNoise; private readonly NoiseField profileNoise; private readonly BicubicInterpolator interpolator; private readonly RiverRenderer riverRenderer; private readonly CliffRenderer cliffRenderer; private readonly SplatRenderer splatRenderer; private readonly BiomeRenderer biomeRenderer; private readonly ClutterRenderer clutterRenderer; private readonly BarycentricInterpolator forestationInterpolator; public WorldRenderer(RandomDirectory random, CommonConfig commonConfig, Config config, WorldState state, HeightMap heightMap) { this.commonConfig = commonConfig; this.config = config; this.state = state; biomeWarpNoise = random.WarpNoise("Warp", commonConfig.tileSpacing, config.biomeWarp); profileNoise = config.profileNoise.bias + random.BiasedNoise2D("ProfileNoise", config.profileNoise.featureSize).Fractal(config.profileNoise.fractal); interpolator = new BicubicInterpolator(heightMap.range, heightMap.resolution, heightMap.data, config.interpolationMethod); riverRenderer = new RiverRenderer(commonConfig.worldSize, config.rivers, state.rivers); biomeRenderer = new BiomeRenderer(random.Sub("BiomeRenderer"), config.biomes, GetForestation); splatRenderer = new SplatRenderer(random.Sub("SplatRenderer"), config.splats, commonConfig.tileSpacing, commonConfig.oceanDepth, state.splats); cliffRenderer = new CliffRenderer(random.Sub("CliffRenderer"), config.cliffs); clutterRenderer = new ClutterRenderer(random.Sub("ClutterNoise"), config.clutter); forestationInterpolator = new BarycentricInterpolator(state.tiling, state.interpolationMesh); } public Biome GetBiome(Rfloat2 pos) { return state.tiling.GetTile(pos + biomeWarpNoise.Sample(pos))?.Data(state.biomes) ?? Biome.Ocean; } private Biome GetBiome(Rfloat2 pos, ref Span<(Biome, double)> weights, out int count) { count = 0; Span<(int, double)> result = stackalloc(int, double)[3]; if (!state.interpolationMesh.GetSortedVertices(pos, ref result)) { return Biome.Ocean; } for (int i = 0; i < 3; i++) { var (num, num2) = result[i]; if (num >= state.tiling.Count) { continue; } Biome byVertexId = state.biomes.GetByVertexId(num); int num3 = count; if (num3 <= 0) { if (num3 == 0) { weights[count++] = (byVertexId, num2); continue; } } else { if (weights[0].Item1 == byVertexId) { weights[0] = (byVertexId, weights[0].Item2 + num2); continue; } if (num3 > 1 && weights[1].Item1 == byVertexId) { weights[1] = (byVertexId, weights[1].Item2 + num2); continue; } } weights[count++] = (byVertexId, num2); } if (result[0].Item1 >= state.tiling.Count) { return Biome.Ocean; } return state.biomes.GetByVertexId(result[0].Item1); } public IWorldGen.PointData RenderPoint(Rfloat2 pos) { Rfloat2 rfloat = biomeWarpNoise.Sample(pos); double profileVariance = profileNoise.Sample(pos); Span<(Biome, double)> weights = stackalloc(Biome, double)[3]; int count; Biome biome = GetBiome(pos + rfloat, ref weights, out count); double num = interpolator.Interpolate(pos, out var gradient, commonConfig.oceanDepth); double slope = gradient.Length; double cliffiness = 1.0; Span span = stackalloc double[3]; double num2 = 0.0; for (int i = 0; i < count; i++) { (Biome, double) tuple = weights[i]; Biome item = tuple.Item1; double item2 = tuple.Item2; num2 += item2 * biomeRenderer.RenderCoarse(item, pos, num, item2, ref span[i]); } num += num2; num = splatRenderer.BlendSplats(pos, num, profileVariance, ref slope, ref cliffiness); num = riverRenderer.BlendRivers(pos, num, profileVariance, ref slope); num += cliffiness * cliffRenderer.Render(pos, num, slope, profileVariance); BiomeRenderer.RenderResult renderResult = default(BiomeRenderer.RenderResult); for (int j = 0; j < count; j++) { var (biome2, num3) = weights[j]; renderResult += num3 * biomeRenderer.RenderFine(biome2, pos, slope, span[j]); } num += renderResult.fineDelta; num += clutterRenderer.Clutter(pos, renderResult.clutter); num = ShapeHeight(pos, num, renderResult.terracing); IWorldGen.PointData result = default(IWorldGen.PointData); result.biome = biome; result.height = num; result.lava = renderResult.lava; result.fertility = renderResult.fertility; result.dirt = 0.0; result.pave = 0.0; return result; } public double GetForestation(Rfloat2 pos) { return forestationInterpolator.Interpolate(pos, state.forestation); } private double ShapeHeight(Rfloat2 pos, double height, double terracing) { height = ApplyTerracing(height, terracing); height = ApplyWorldEdge(height, pos.Length); return height; } private double ApplyTerracing(double height, double terracing) { if (config.shaping.terracingStep <= 0.0) { return height; } double b = (double)Rmath.RoundToInt(height / config.shaping.terracingStep) * config.shaping.terracingStep; return Rmath.Lerp(terracing, height, b); } private double ApplyWorldEdge(double height, double distanceFromCenter) { return Rmath.Lerp(Rmath.InverseLerp(distanceFromCenter, commonConfig.worldSize - config.shaping.worldEdgeWidth, commonConfig.worldSize), height, config.shaping.offworldDepth); } } [Producer(new string[] { "Rendering/WorldRenderer" })] public class WorldRendererStage : IGeneratorStage { public const string OUTPUT = "Rendering/WorldRenderer"; public const string RANDOM_KEY = "Rendering"; [Random("Rendering")] public RandomDirectory random; [Config] public CommonConfig commonConfig; [Config] public WorldRenderer.Config config; [Resource("WorldState")] public WorldState worldState; [Resource("Rendering/HeightMap")] public HeightMap heightMap; public WorldRenderer Compute() { return new WorldRenderer(random, commonConfig, config, worldState, heightMap); } } } namespace Riverheim.Rendering.Splats { public readonly struct CliffSplat : ISplat { public struct Config { public double fixedFlatRange; public double variableFlatRange; public ProfileUtility.Rational.Config skirt; public Rfloat2 incline; public Direction bulgeDirection; public Rfloat2 bulgeMagnitude; public bool inverted; } private readonly Config config; private readonly DistanceFunctions.Sdf sdf; private readonly Rfloat2 bulgeX; private readonly Rfloat2 bulgeY; public BoundingBox Bbox { get; } public ISplat.SplatBlendMode BlendMode { get { if (!config.inverted) { return ISplat.SplatBlendMode.SMax; } return ISplat.SplatBlendMode.SMin; } } public double Cliffiness => 1.0; public CliffSplat(Config config, DistanceFunctions.Sdf sdf, BoundingBox contour, double targetHeight) { this.config = config; this.sdf = sdf; bulgeX = config.bulgeDirection.ToVector(); bulgeY = config.bulgeDirection.Rotate(Math.PI / 2.0).ToVector(); Bbox = contour.Expand(config.fixedFlatRange + config.variableFlatRange + ProfileUtility.Rational.Inverse(targetHeight, in config.skirt)); } public double Render(Rfloat2 pos, double noise, out double slope, out double t) { t = 0.0; double distance = Rmath.Rectifier(sdf(pos) - config.fixedFlatRange - config.variableFlatRange * noise); double num = ProfileUtility.Rational.Profile(distance, in config.skirt); slope = ProfileUtility.Rational.Derivative(distance, in config.skirt); double num2 = Rfloat2.Dot(pos, config.incline); double num3 = Rfloat2.Dot(pos, bulgeX); double num4 = Rfloat2.Dot(pos, bulgeY); return num + num2 - config.bulgeMagnitude.x * num3 * num3 - config.bulgeMagnitude.y * num4 * num4; } } public readonly struct CragSplat : ISplat { public struct Config { public double radius; public double radiusVariability; public double terrace; public double terraceVariability; public double cliffiness; public ProfileUtility.Rational.Config profile; } private const int NOISE_SEED = 734855; private readonly Config config; private readonly DistanceFunctions.Sdf sdf; public BoundingBox Bbox { get; } public ISplat.SplatBlendMode BlendMode => ISplat.SplatBlendMode.SMax; public double Cliffiness => config.cliffiness; public CragSplat(Config config, double targetHeight) { this.config = config; sdf = DistanceFunctions.DiskSdf(Rfloat2.Zero, config.radius, out var contour); Bbox = contour.Expand(config.radiusVariability + ProfileUtility.Rational.Inverse(targetHeight, in config.profile)); } public double Render(Rfloat2 pos, double profileNoise, out double slope, out double t) { t = 0.0; double distance = Rmath.Rectifier(sdf(pos) - config.radiusVariability * profileNoise); double num = ProfileUtility.Rational.Profile(distance, in config.profile); slope = ProfileUtility.Rational.Derivative(distance, in config.profile); if (config.terrace <= 0.0) { return num; } double num2 = config.terrace * config.profile.slope; double num3 = 2.0 / num2; double num4 = num2 * GradientNoise.Sample(734855u, num3 * pos.x, num3 * pos.y); double num5 = (num + config.terraceVariability * num4) / config.terrace; int num6 = Rmath.FloorToInt(num5); return config.terrace * ((double)num6 + Rmath.Smooth5(num5 - (double)num6)); } } public readonly struct FlatSplat : ISplat { public struct Config { public double blendRange; public double contourVarianceRange; } private readonly Config config; private readonly DistanceFunctions.Sdf sdf; public BoundingBox Bbox { get; } public ISplat.SplatBlendMode BlendMode => ISplat.SplatBlendMode.Mix; public double Cliffiness => 0.0; public FlatSplat(Config config, DistanceFunctions.Sdf sdf, BoundingBox contour) { this.config = config; this.sdf = sdf; Bbox = contour.Expand(config.blendRange + config.contourVarianceRange); } public double Render(Rfloat2 pos, double noise, out double slope, out double t) { double value = Rmath.Rectifier(sdf(pos) - config.contourVarianceRange * noise); slope = 0.0; t = Rmath.Smooth3(Rmath.InverseLerp(value, config.blendRange, 0.0)); return 0.0; } } public interface ISplat { public enum SplatBlendMode { SMin, SMax, Mix } BoundingBox Bbox { get; } SplatBlendMode BlendMode { get; } double Cliffiness { get; } double Render(Rfloat2 pos, double noise, out double slope, out double t); } public class SplatObject { private readonly ISplat impl; private readonly Rfloat2 pos; private readonly double height; public ISplat.SplatBlendMode BlendMode => impl.BlendMode; public double Cliffiness => impl.Cliffiness; public SplatObject(ISplat impl, Rfloat2 pos, double height) { this.impl = impl; this.pos = pos; this.height = height; } public double Render(Rfloat2 pos, double noise, out double slope, out double t) { return impl.Render(pos - this.pos, noise, out slope, out t) + height; } } } namespace Riverheim.Rendering.Rivers { public struct RiverPointData { public double width; public double blendingDepth; public ProfileUtility.BiasedRational.Config rationalProfile; public static RiverPointData Lerp(double t, RiverPointData lhs, RiverPointData rhs) { RiverPointData result = default(RiverPointData); result.width = Rmath.Lerp(t, lhs.width, rhs.width); result.blendingDepth = Rmath.Lerp(t, lhs.blendingDepth, rhs.blendingDepth); result.rationalProfile = ProfileUtility.BiasedRational.Config.Lerp(t, in lhs.rationalProfile, in rhs.rationalProfile); return result; } } public class RiverRenderer { [Serializable] public struct WidthConfig { public double depthPower; public double minWidth; } [Serializable] public struct ProfileConfig { public double minDepth; public double maxBankHeight; public double lerpingMargin; public double depthSmoothness; public double widthToDepth; public double bankSmoothness; public double noiseAmplitude; public double noiseKickInStart; public double noiseKickInEnd; } [Serializable] public struct Config { public bool isEnabled; public double spatialIndexSpacing; public double sdfMergingRange; public WidthConfig width; public TesselationConfig tesselation; public ProfileConfig profile; } private readonly Config config; private readonly RiverSpatialIndex riverSpatialIndex; private readonly Dictionary> riverData; public RiverRenderer(double worldSize, Config config, River[] rivers) { this.config = config; riverData = BuildRiverData(rivers); riverSpatialIndex = BuildSpatialIndex(worldSize, rivers); } private Dictionary> BuildRiverData(River[] rivers) { Dictionary> dictionary = new Dictionary>(); for (int i = 0; i < rivers.Length; i++) { River river = rivers[i]; RiverPointData[] array = new RiverPointData[river.spline.SegmentCount + 1]; for (int j = 0; j < array.Length; j++) { array[j] = MakeRiverPointData(river.data[j]); } dictionary[i] = new PolylineData(array, RiverPointData.Lerp); } return dictionary; } private RiverSpatialIndex BuildSpatialIndex(double worldSize, River[] rivers) { RiverSpatialIndex.Builder builder = new RiverSpatialIndex.Builder(config.spatialIndexSpacing, BoundingBox.ZeroCentered(new Rfloat2(2.0 * worldSize))); RiverTesselator riverTesselator = new RiverTesselator(config.tesselation); for (int i = 0; i < rivers.Length; i++) { RiverTesselator.Vertex[] array = riverTesselator.GenerateVertices(rivers[i]).ToArray(); foreach (RiverSegment item in array.Zip(array.Skip(1), MakeRiverSegment)) { builder.Add(i, item); } } return builder.Build(); } private double RiverPointWidth(RiverVertexData vertexData) { return 0.5 * Rmath.Lerp(Rmath.Pow(vertexData.depth, config.width.depthPower), config.width.minWidth, vertexData.width); } private RiverPointData MakeRiverPointData(RiverVertexData vertexData) { double width = RiverPointWidth(vertexData); RiverPointData result = default(RiverPointData); result.width = width; result.blendingDepth = vertexData.depth; result.rationalProfile = BuildProfileConfig(width, vertexData.steepness, vertexData.depth); return result; } private ProfileUtility.BiasedRational.Config BuildProfileConfig(double width, double steepness, double blendingDepth) { double num = width / config.profile.widthToDepth; num = Rmath.Lerp(blendingDepth, num, SmoothMax.Cubic(config.profile.minDepth, num, config.profile.depthSmoothness)); return ProfileUtility.BiasedRational.Config.FromMetrics(width, num, steepness); } private RiverSegment MakeRiverSegment(RiverTesselator.Vertex p1, RiverTesselator.Vertex p2) { double r = RiverBankRange(RiverPointWidth(p1.data), p1.data.steepness, p1.data.depth); double r2 = RiverBankRange(RiverPointWidth(p2.data), p2.data.steepness, p2.data.depth); return new RiverSegment(p1.position, p2.position, p1.splinePos, p2.splinePos, r, r2); } private double RiverBankRange(double width, double steepness, double blendingDepth) { double maxBankHeight = this.config.profile.maxBankHeight; ProfileUtility.BiasedRational.Config config = BuildProfileConfig(width, steepness, blendingDepth); return ProfileUtility.BiasedRational.Inverse(maxBankHeight, in config); } private bool ProcessRiver(Rfloat2 pos, RiverSpatialIndex.LocalRiver river, out double distanceTo, out PolylineProgress progress) { RiverSegment[] segments = river.segments; Span span = stackalloc double[segments.Length]; Span span2 = stackalloc double[segments.Length]; int num = -1; double num2 = double.MaxValue; for (int i = 0; i < segments.Length; i++) { span[i] = segments[i].SqrDistance(pos, out var t); double num3 = Rmath.Lerp(t, segments[i].r1, segments[i].r2); if (span[i] > num3 * num3) { span[i] = double.MaxValue; continue; } if (span[i] < num2) { num = i; num2 = span[i]; } span2[i] = t; } if (num == -1) { distanceTo = 0.0; progress = default(PolylineProgress); return false; } double num4 = Rmath.Lerp(span2[num], segments[num].splinePos1, segments[num].splinePos2); double num5 = 1.0; double num6 = Rmath.Sqrt(num2); for (int j = 0; j < segments.Length; j++) { if (j != num) { double num7 = Rmath.Sqrt(span[j]) - num6; if (!(num7 > config.sdfMergingRange)) { double num8 = 1.0 - num7 / config.sdfMergingRange; double num9 = num8 * num8 * num8; num4 += Rmath.Lerp(span2[j], segments[j].splinePos1, segments[j].splinePos2) * num9; num5 += num9; } } } distanceTo = num6; progress = num4 / num5; return true; } public double BlendRivers(Rfloat2 pos, double height, double profileVariance, ref double slope) { if (!config.isEnabled) { return height; } RiverSpatialIndex.LocalRiver[] array = riverSpatialIndex.Query(pos); for (int i = 0; i < array.Length; i++) { RiverSpatialIndex.LocalRiver river = array[i]; if (ProcessRiver(pos, river, out var distanceTo, out var progress)) { height = BlendRiver(height, distanceTo, riverData[river.id].Evaluate(progress), profileVariance, ref slope); } } return height; } private double BlendRiver(double height, double distance, RiverPointData data, double profileVariance, ref double slope) { double num = config.profile.noiseAmplitude * Rmath.InverseLerp(distance, data.width * config.profile.noiseKickInStart, data.width * config.profile.noiseKickInEnd); distance += num * profileVariance; double num2 = RiverBankProfile(distance, data) + (1.0 - data.blendingDepth) * height; double t; double a = SmoothMin.Cubic(num2, height, config.profile.bankSmoothness, out t); slope = Rmath.Lerp(t, RiverBankProfileDerivative(distance, data), slope); return Rmath.SmoothStep(Rmath.InverseLerp(num2, config.profile.maxBankHeight - config.profile.lerpingMargin, config.profile.maxBankHeight), a, height); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double RiverBankProfile(double distance, RiverPointData data) { return ProfileUtility.BiasedRational.Profile(distance, in data.rationalProfile); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static double RiverBankProfileDerivative(double distance, RiverPointData data) { return ProfileUtility.BiasedRational.Derivative(distance, in data.rationalProfile); } } public readonly struct RiverSegment { private readonly Rfloat2 p1; private readonly Rfloat2 p2; public readonly double splinePos1; public readonly double splinePos2; public readonly double r1; public readonly double r2; public BoundingBox Bbox { get; } public RiverSegment(Rfloat2 p1, Rfloat2 p2, double splinePos1, double splinePos2, double r1, double r2) { this.p1 = p1; this.p2 = p2; this.splinePos1 = splinePos1; this.splinePos2 = splinePos2; this.r1 = r1; this.r2 = r2; double num = Rmath.Max(r1, r2); BoundingBox boundingBox = new BoundingBox(new List { p1, p2 }); Bbox = new BoundingBox(boundingBox.Pos - num, boundingBox.Size + 2.0 * num); } public double SqrDistance(Rfloat2 pos, out double t) { return DistanceFunctions.SegmentSquared(pos, p1, p2, out t); } public bool HitTest(BoundingBox rect) { return rect.Overlaps(Bbox); } } public class RiverSpatialIndex { public readonly struct LocalRiver { public readonly int id; public readonly RiverSegment[] segments; public LocalRiver(int id, RiverSegment[] segments) { this.id = id; this.segments = segments; } } public class Builder { private readonly double cellSize; private readonly Rfloat2 offset; private readonly Rint2 size; private readonly Dictionary>[] segments; public Builder(double cellSize, BoundingBox bounds) { this.cellSize = cellSize; offset = bounds.Pos; size = (bounds.Size / cellSize).CeilToInt(); segments = new Dictionary>[size.x * size.y]; for (int i = 0; i < segments.Length; i++) { segments[i] = new Dictionary>(); } } public RiverSpatialIndex Build() { LocalRiver[][] array = new LocalRiver[size.x * size.y][]; for (int i = 0; i < segments.Length; i++) { List list = new List(); foreach (int key in segments[i].Keys) { list.Add(new LocalRiver(key, segments[i][key].ToArray())); } list.Sort((LocalRiver a, LocalRiver b) => a.id.CompareTo(b.id)); array[i] = list.ToArray(); } return new RiverSpatialIndex(cellSize, offset, size, array); } public void Add(int riverId, RiverSegment segment) { BoundingBox bbox = segment.Bbox; bbox.x -= offset.x; bbox.y -= offset.y; for (int i = Index(bbox.x); i <= Index(bbox.x + bbox.w); i++) { for (int j = Index(bbox.y); j <= Index(bbox.y + bbox.h); j++) { TryAdd(new Rint2(i, j), riverId, segment); } } } private void TryAdd(Rint2 index, int riverId, RiverSegment segment) { if (segment.HitTest(CellRect(index)) && index.x >= 0 && index.y >= 0 && index.x < size.x && index.y < size.y) { Dictionary> dictionary = segments[index.x * size.y + index.y]; if (!dictionary.ContainsKey(riverId)) { dictionary.Add(riverId, new List()); } dictionary[riverId].Add(segment); } } private int Index(double x) { return Rmath.FloorToInt(x / cellSize); } private BoundingBox CellRect(Rint2 index) { return new BoundingBox(cellSize * (Rfloat2)index + offset, cellSize * Rfloat2.One); } } private readonly double cellSize; private readonly Rfloat2 offset; private readonly Rint2 size; private readonly LocalRiver[][] data; private readonly LocalRiver[] sentinel; private RiverSpatialIndex(double cellSize, Rfloat2 offset, Rint2 size, LocalRiver[][] data) { this.cellSize = cellSize; this.offset = offset; this.size = size; this.data = data; sentinel = Array.Empty(); } public LocalRiver[] Query(Rfloat2 pos) { pos -= offset; int num = Index(pos.x); int num2 = Index(pos.y); if (num < 0 || num >= size.x || num2 < 0 || num2 >= size.y) { return sentinel; } return data[num * size.y + num2]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] private int Index(double x) { return Rmath.FloorToInt(x / cellSize); } } [Serializable] public struct TesselationConfig { public double minStep; public double maxStep; public double stepRatio; } public class RiverTesselator { public readonly struct Vertex { public readonly PolylineProgress splinePos; public readonly Rfloat2 position; public readonly RiverVertexData data; public Vertex(PolylineProgress splinePos, Rfloat2 position, RiverVertexData data) { this.splinePos = splinePos; this.position = position; this.data = data; } } [CompilerGenerated] private sealed class d__3 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private Vertex <>2__current; private int <>l__initialThreadId; private River river; public River <>3__river; public RiverTesselator <>4__this; private IEnumerator<(PolylineProgress, SplineWalk.StepEmitter)> <>7__wrap1; private SplineWalk.StepEmitter 5__3; private RiverVertexData 5__4; Vertex IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__3(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 1) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = null; 5__3 = null; <>1__state = -2; } private bool MoveNext() { try { int num = <>1__state; RiverTesselator riverTesselator = <>4__this; switch (num) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = river.spline.Walk().GetEnumerator(); <>1__state = -3; break; case 1: <>1__state = -3; 5__3.SetStep(Rmath.Min(riverTesselator.config.maxStep, Rmath.Max(riverTesselator.config.minStep, (double)5__4.width * (double)5__4.depth * riverTesselator.config.stepRatio))); 5__3 = null; break; } if (<>7__wrap1.MoveNext()) { (PolylineProgress, SplineWalk.StepEmitter) current = <>7__wrap1.Current; PolylineProgress item = current.Item1; 5__3 = current.Item2; Rfloat2 position = river.spline.EvaluatePosition(item); 5__4 = river.data.Evaluate(item); <>2__current = new Vertex(item, position, 5__4); <>1__state = 1; return true; } <>m__Finally1(); <>7__wrap1 = null; return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__3 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__3(0) { <>4__this = <>4__this }; } d__.river = <>3__river; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } private readonly TesselationConfig config; public RiverTesselator(TesselationConfig config) { this.config = config; } [IteratorStateMachine(typeof(d__3))] public IEnumerable GenerateVertices(River river) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__3(-2) { <>4__this = this, <>3__river = river }; } } } namespace Riverheim.Rendering.Biomes { public class CommonBiomeRenderer : ISingleBiomeRenderer { public class QuadNoise { [Serializable] public struct Config { public double coarse1; public double coarse2; public double fine1; public double fine2; public double offset; } private const double NOISE_MULT_AVERAGE = 0.25; private const double FINAL_AVERAGE = 9.0 / 32.0; private readonly Config config; private readonly NoiseField noise1; private readonly NoiseField noise2; private readonly NoiseField noise3; private readonly NoiseField noise4; public QuadNoise(RandomDirectory random, Config config) { this.config = config; noise1 = random.BiasedNoise2D("Noise1", config.coarse1); noise2 = random.BiasedNoise2D("Noise2", config.coarse2); noise3 = random.BiasedNoise2D("Noise3", config.fine1); noise4 = random.BiasedNoise2D("Noise4", config.fine2); } public double Sample(Rfloat2 pos, bool compensation = true) { double offset = config.offset; double num = (noise1.Sample(pos) + offset) * (noise2.Sample(pos) + offset); double num2 = noise3.Sample(pos) * noise4.Sample(pos); return num * (1.0 + 0.5 * num2) - offset * offset - (compensation ? (9.0 / 32.0) : 0.0); } } [Serializable] public struct Config { public double coarseAmplitude; public ClutterRenderer.Params clutter; public double fertility; } private readonly Config config; private readonly QuadNoise coarseNoise; public CommonBiomeRenderer(Config config, QuadNoise coarseNoise) { this.config = config; this.coarseNoise = coarseNoise; } public double RenderCoarse(Rfloat2 pos, double height, double saturation, ref double meta) { return config.coarseAmplitude * coarseNoise.Sample(pos); } public BiomeRenderer.RenderResult RenderFine(Rfloat2 pos, double slope, double meta) { BiomeRenderer.RenderResult result = default(BiomeRenderer.RenderResult); result.clutter = config.clutter; result.fertility = config.fertility; return result; } } public interface ISingleBiomeRenderer { double RenderCoarse(Rfloat2 pos, double height, double saturation, ref double meta); BiomeRenderer.RenderResult RenderFine(Rfloat2 pos, double slope, double meta); } public class LavaplainRenderer : ISingleBiomeRenderer { [Serializable] public struct Config { public double lavaCellScale; public NoiseField.FractalConfig lavaCellFractal; public double lavaCellMultiplier; public int lavaCellPower; public double lavaGradScale; public NoiseField.FractalConfig lavaGradFractal; public double lavaGradRemapMin; public double lavaGradRemapMax; public int lavaGradPower; public Rmath.RemapConfig lavaGradHeightRemap; public double lavaDepressionNoiseScale; public double lavaDepressionNoisePower; public double lavaMinDepression; public double lavaMaxDepression; public double lavaMinHeightClamp; public ClutterRenderer.Params clutter; } private readonly Config config; private readonly NoiseField lavaCellular; private readonly NoiseField lavaGradient; private readonly NoiseField lavaInner; public LavaplainRenderer(RandomDirectory random, Config config) { this.config = config; lavaCellular = config.lavaCellFractal.GetMultiplier() * random.CellularNoise2D("LavaCellular", config.lavaCellScale).Fractal(config.lavaCellFractal); lavaGradient = config.lavaGradFractal.GetMultiplier() * random.BiasedNoise2D("LavaGradient", config.lavaGradScale).Fractal(config.lavaGradFractal); lavaInner = random.BiasedNoise2D("LavaInternal", config.lavaDepressionNoiseScale); } private static double BlendOverlay(double a, double b) { if (a >= 0.5) { return 1.0 - 2.0 * (1.0 - a) * (1.0 - b); } return 2.0 * a * b; } public double RenderCoarse(Rfloat2 pos, double height, double saturation, ref double lava) { double num = lavaCellular.Sample(pos); num = 0.5 * (num + 1.0); num = Rmath.Clamp01(Rmath.Pow(num, config.lavaCellPower) * config.lavaCellMultiplier); double num2 = lavaGradient.Sample(pos); num2 *= Rmath.Remap(height, config.lavaGradHeightRemap); num2 = Rmath.InverseLerp(num2, config.lavaGradRemapMin, config.lavaGradRemapMax); num2 = Rmath.Pow(num2, config.lavaGradPower); lava = BlendOverlay(num2, num) * Rmath.Clamp01(0.5 * height - config.lavaMinHeightClamp); lava *= Rmath.Smooth3(Rmath.InverseLerp(saturation, 0.8, 1.0)); return 0.0 - lava * Rmath.Min(Rmath.Lerp(Rmath.Pow(lavaInner.Sample(pos), config.lavaDepressionNoisePower), config.lavaMinDepression, config.lavaMaxDepression), height - config.lavaMinHeightClamp); } public BiomeRenderer.RenderResult RenderFine(Rfloat2 _1, double _, double lava) { BiomeRenderer.RenderResult result = default(BiomeRenderer.RenderResult); result.clutter = (1.0 - lava) * config.clutter; result.lava = lava; return result; } } public class MeadowsRenderer : ISingleBiomeRenderer { [Serializable] public struct Config { public double coarseMagnitude; public double cliffDensityThreshold; public double cliffDensityMargin; public double cliffMaxHeight; public double cliffNoiseScale; public double cliffNoiseMargin; public Rmath.Range cliffHeightStartRange; public ClutterRenderer.Params clutter; } private readonly Config config; private readonly Func forestationMap; private readonly CommonBiomeRenderer.QuadNoise commonCoarseNoise; private readonly NoiseField forestCliffNoise; public MeadowsRenderer(RandomDirectory random, Config config, Func forestationMap, CommonBiomeRenderer.QuadNoise commonCoarseNoise) { this.config = config; this.forestationMap = forestationMap; this.commonCoarseNoise = commonCoarseNoise; forestCliffNoise = random.Noise2D("ForestCliff", config.cliffNoiseScale).Fractal(3); } private double GetCliff(Rfloat2 pos, double baseHeight, double saturation) { double num = config.cliffNoiseMargin * forestCliffNoise.Sample(pos); double num2 = config.cliffDensityThreshold + num; double num3 = Rmath.InverseLerp(forestationMap(pos), num2 - config.cliffDensityMargin, num2 + config.cliffDensityMargin); double num4 = Rmath.InverseLerp(baseHeight, config.cliffHeightStartRange); return config.cliffMaxHeight * saturation * Rmath.Smooth3(num4 * num3); } public double RenderCoarse(Rfloat2 pos, double height, double saturation, ref double _) { return config.coarseMagnitude * commonCoarseNoise.Sample(pos) + GetCliff(pos, height, saturation); } public BiomeRenderer.RenderResult RenderFine(Rfloat2 _1, double _2, double _3) { BiomeRenderer.RenderResult result = default(BiomeRenderer.RenderResult); result.clutter = config.clutter; result.fertility = 1.0; return result; } } public class MistgladeRenderer : ISingleBiomeRenderer { [Serializable] public struct Config { public double courseScale; public double coarseMagnitude; public double slabScale; public double slabHeight; public NoiseField.FractalConfig slabFractal; public Rmath.Range slabHeightRemap; public double slabCrackScale; public double slabCrackDepth; public ClutterRenderer.Params clutter; public ClutterRenderer.Params slabClutter; public Rmath.Range slopeToFertilityRemap; } private readonly Config config; private readonly NoiseField coarseNoise; private readonly NoiseField slabNoise; private readonly NoiseField slabCrackNoise; public MistgladeRenderer(RandomDirectory random, Config config) { this.config = config; coarseNoise = random.Noise2D("Coarse", config.courseScale); slabNoise = random.BiasedNoise2D("Slab", config.slabScale).Fractal(config.slabFractal); slabCrackNoise = random.CellularNoise2DDiv("SlabCracks", config.slabCrackScale); } public double RenderCoarse(Rfloat2 pos, double _1, double _2, ref double slab) { slab = Rmath.Smooth3(Rmath.Sqrt(Rmath.InverseLerp(slabNoise.Sample(pos), config.slabHeightRemap))); return config.coarseMagnitude * coarseNoise.Sample(pos) + config.slabHeight * slab; } public BiomeRenderer.RenderResult RenderFine(Rfloat2 pos, double slope, double slab) { double a = 1.0 - Rmath.InverseLerp(slab, 0.0, 0.1); a = Rmath.Min(a, Rmath.InverseLerp(slope, config.slopeToFertilityRemap)); double num = slabCrackNoise.Sample(pos); num = Rmath.InverseLerp(num * num, 0.3, 1.0); BiomeRenderer.RenderResult result = default(BiomeRenderer.RenderResult); result.clutter = (1.0 - slab) * config.clutter + slab * config.slabClutter; result.fertility = a; result.fineDelta = (0.0 - slab) * config.slabCrackDepth * num; return result; } } public class MistlandsRenderer : ISingleBiomeRenderer { [Serializable] public struct Config { public double coarseMagnitude; public CommonBiomeRenderer.QuadNoise.Config coarse; public double pow; public double bias; public ClutterRenderer.Params valleyClutter; public ClutterRenderer.Params hillClutter; public Rmath.Range slopeToFertilityRemap; public double terracingMult; } private readonly Config config; private readonly CommonBiomeRenderer.QuadNoise coarseNoise; public MistlandsRenderer(RandomDirectory random, Config config) { this.config = config; coarseNoise = new CommonBiomeRenderer.QuadNoise(random.Sub("CoarseNoise"), config.coarse); } public double RenderCoarse(Rfloat2 pos, double _, double saturation, ref double hilliness) { double num = coarseNoise.Sample(pos, compensation: false); double num2 = saturation * ((num > 0.0) ? Rmath.Pow(num, config.pow) : num); hilliness = Rmath.Clamp01(num2 * 8.0); return config.coarseMagnitude * num2 + config.bias; } public BiomeRenderer.RenderResult RenderFine(Rfloat2 _, double slope, double hilliness) { double a = Rmath.InverseLerp(hilliness, 0.1, 0.3) - 1.2 * hilliness; a = Rmath.Min(a, Rmath.InverseLerp(slope, config.slopeToFertilityRemap)); BiomeRenderer.RenderResult result = default(BiomeRenderer.RenderResult); result.clutter = (1.0 - hilliness) * config.valleyClutter + hilliness * config.hillClutter; result.fertility = a; result.terracing = config.terracingMult * hilliness; return result; } } public class SwampRenderer : ISingleBiomeRenderer { [Serializable] public struct Config { public double coarseMagnitude; public double coarseScale1; public double coarseScale2; public double pathVerticalCutoff; public double pathBandWidth; public double pathBandDepth; public double pathNoiseScale; public double pathNoiseIntensity; public VectorNoiseField.RelativeWarpConfig pathWarp; public double pathBlendingStrength; public ClutterRenderer.Params clutter; } private readonly Config config; private readonly VectorNoiseField warpNoise; private readonly NoiseField pathNoise; private readonly NoiseField coarseNoise1; private readonly NoiseField coarseNoise2; public SwampRenderer(RandomDirectory random, Config config) { this.config = config; warpNoise = random.WarpNoise("Swamp/Warp", config.pathNoiseScale, config.pathWarp); pathNoise = random.CellularNoise2DDiv("Swamp/Paths", config.pathNoiseScale, config.pathNoiseIntensity); coarseNoise1 = random.BiasedNoise2D("CoarseNoise1", config.coarseScale1); coarseNoise2 = random.BiasedNoise2D("CoarseNoise2", config.coarseScale2); } private double Binoise(Rfloat2 pos) { return coarseNoise1.Sample(pos) * coarseNoise2.Sample(pos) - 0.25; } public double RenderCoarse(Rfloat2 pos, double _1, double _2, ref double _3) { double a = config.coarseMagnitude * Binoise(pos); double value = pathNoise.Sample(pos + warpNoise.Sample(pos)); value = Rmath.Lerp(Rmath.Smooth5(Rmath.InverseLerp(value, 0.0, config.pathVerticalCutoff)), config.pathBandDepth - 0.5 * config.pathBandWidth, config.pathBandDepth + 0.5 * config.pathBandWidth); return SmoothMax.Cubic(a, value, config.pathBlendingStrength); } public BiomeRenderer.RenderResult RenderFine(Rfloat2 _1, double _2, double _3) { BiomeRenderer.RenderResult result = default(BiomeRenderer.RenderResult); result.clutter = config.clutter; result.fertility = 1.0; return result; } } } namespace Riverheim.Pipeline { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum)] public sealed class ForkingDimensionAttribute : Attribute { public readonly string dimension; public readonly int laneCount; public ForkingDimensionAttribute(string dimension, int laneCount) { this.dimension = dimension ?? throw new PipelineError("fork dimension must not be null"); this.laneCount = laneCount; if (laneCount <= 0) { throw new PipelineError("Lane count must be greater than 0 for dimension '" + dimension + "'"); } } public ForkingDimensionAttribute(string dimension, Type enumType) : this(dimension, Enum.GetNames(enumType).Length) { } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] public sealed class CompositeConfigAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class)] public sealed class ProducerAttribute : Attribute { public readonly string[] resourceIds; public ProducerAttribute(params string[] resourceIds) { if (resourceIds.Length == 0) { throw new PipelineError("stage must produce at least one resource"); } this.resourceIds = resourceIds; } } [AttributeUsage(AttributeTargets.Class)] public sealed class ForkAttribute : Attribute { public readonly string forkDimension; public ForkAttribute(string forkDimension) { this.forkDimension = forkDimension ?? throw new PipelineError("fork dimension must not be null"); } } [AttributeUsage(AttributeTargets.Field)] public sealed class LaneAttribute : Attribute { } [AttributeUsage(AttributeTargets.Field)] public sealed class RandomAttribute : Attribute { public readonly string key; public RandomAttribute(string key) { this.key = key ?? throw new PipelineError("randomize key must not be null"); } } [AttributeUsage(AttributeTargets.Field)] public sealed class ConfigAttribute : Attribute { } [AttributeUsage(AttributeTargets.Field)] public sealed class LaneConfigAttribute : Attribute { } [AttributeUsage(AttributeTargets.Field)] public sealed class ResourceAttribute : Attribute { public readonly string resourceId; public ResourceAttribute(string resourceId) { this.resourceId = resourceId ?? throw new PipelineError("resource name must not be null"); } } internal class ComputationGraph { private readonly List all = new List(); private readonly List roots = new List(); private readonly Dictionary> configDependency = new Dictionary>(); private readonly Dictionary> requires = new Dictionary>(); private readonly Dictionary> blocks = new Dictionary>(); public int TotalResources => all.Count; public ComputationGraph(string[] outputResourceIds) { Dictionary allResourcesFor = Registry.GetAllResourcesFor(outputResourceIds); foreach (string key in allResourcesFor.Keys) { blocks[key] = new List(); requires[key] = new List(); } foreach (string key2 in allResourcesFor.Keys) { all.Add(key2); ResourceRef resourceRef = allResourcesFor[key2]; if (resourceRef.producer.ConsumerOf.Count == 0) { roots.Add(key2); } foreach (string item in resourceRef.producer.ConsumerOf) { blocks[item].Add(key2); requires[key2].Add(item); } if (resourceRef.producer.Configs.Count <= 0) { continue; } foreach (ConfigRef config in resourceRef.producer.Configs) { if (!configDependency.ContainsKey(config)) { configDependency[config] = new List(); } configDependency[config].Add(key2); } } if (roots.Count == 0) { throw new PipelineError("All stages required to compute " + string.Join(", ", outputResourceIds) + " have dependencies, nowhere to start"); } } public IEnumerable GetRoots() { return roots; } public IEnumerable GetAllResources() { return all; } public IEnumerable GetRequiredResources(string resourceId) { return requires[resourceId]; } public IEnumerable GetBlockedResources(string resourceId) { return blocks[resourceId]; } public IEnumerable GetBlockedResourcesRecursive(ConfigRef configRef) { HashSet hashSet = new HashSet(); Queue queue = new Queue(); foreach (string item in configDependency[configRef]) { if (hashSet.Add(item)) { queue.Enqueue(item); } } while (queue.Count > 0) { string key = queue.Dequeue(); foreach (string item2 in blocks[key]) { if (hashSet.Add(item2)) { queue.Enqueue(item2); } } } return hashSet.AsEnumerable(); } } internal class GeneratorStage { private readonly struct Resource { private readonly object value; private readonly bool useAsArray; public Resource(object value, bool useAsArray) { this.value = value; this.useAsArray = useAsArray; } public object GetValue() { return value; } public object GetValue(int lane) { if (!useAsArray) { return value; } return ((Array)value).GetValue(lane); } } private readonly StageRef stageRef; private readonly int laneCount; public GeneratorStage(StageRef stageRef, Dictionary forkingLaneCounts) { this.stageRef = stageRef; if (stageRef.IsForked) { if (!forkingLaneCounts.TryGetValue(stageRef.forkDimension, out laneCount)) { throw new PipelineError("Pipeline forking rules don't specify lane count for dimension '" + stageRef.forkDimension + "', required by stage '" + stageRef.type.Name + "'"); } } else { laneCount = 1; } foreach (StageRef.ResourceFieldRef resourceField in stageRef.resourceFields) { ResourceRef resource = Registry.GetResource(resourceField.resourceId); Type fieldType = resourceField.field.FieldType; Type type = resource.type; if (resource.IsForked && (!stageRef.IsForked || stageRef.forkDimension != resource.ForkDimension)) { type = type.MakeArrayType(); } if (fieldType != type) { throw new PipelineError("Field '" + resourceField.field.Name + "' in stage '" + stageRef.type.Name + "' requests resource '" + resourceField.resourceId + "' of type '" + type.Name + "' but has type '" + fieldType.Name + "'"); } } } private Dictionary GetConfigs(PipelineCache cache) { Dictionary dictionary = new Dictionary(); foreach (ConfigRef config in stageRef.Configs) { dictionary[config.type] = cache.GetConfig(config); } return dictionary; } private Dictionary GetDependencies(PipelineCache cache) { Dictionary dictionary = new Dictionary(); foreach (string item in stageRef.ConsumerOf) { if (!cache.TryGet(item, out var resource)) { throw new PipelineError("Internal error! Cache missing for '" + item + "'"); } dictionary[item] = new Resource(resource, stageRef.IsForked && Registry.GetResource(item).ForkDimension == stageRef.forkDimension); } return dictionary; } public Func> GetPackagedTask(RandomDirectory random, PipelineCache cache) { Dictionary configs = GetConfigs(cache); Dictionary dependencies = GetDependencies(cache); if (stageRef.IsForked) { object[] instances = new object[laneCount]; for (int i = 0; i < laneCount; i++) { instances[i] = ConstructInstanceForLane(random, i, configs, dependencies); } return delegate { Dictionary dictionary = new Dictionary(); for (int j = 0; j < laneCount; j++) { foreach (KeyValuePair item in Compute(instances[j])) { item.Deconstruct(out var key, out var value); string key2 = key; object obj = value; Type type = obj.GetType(); if (!dictionary.ContainsKey(key2)) { dictionary[key2] = Array.CreateInstance(type, laneCount); } ((Array)dictionary[key2]).SetValue(obj, j); } } return dictionary; }; } object instance = ConstructInstance(random, configs, dependencies); return () => Compute(instance); } private Dictionary Compute(object instance) { object obj = stageRef.computeMethod.Invoke(instance, Array.Empty()); Dictionary dictionary = new Dictionary(); if (stageRef.producerOf.Count == 1) { dictionary[stageRef.producerOf[0]] = obj; } else { ITuple tuple = (ITuple)obj; for (int i = 0; i < stageRef.producerOf.Count; i++) { dictionary[stageRef.producerOf[i]] = tuple[i]; } } return dictionary; } private object ConstructInstance(RandomDirectory random, Dictionary configs, Dictionary dependencies) { object obj = stageRef.constructor.Invoke(Array.Empty()); foreach (StageRef.RandomFieldRef randomizedField in stageRef.randomizedFields) { randomizedField.field.SetValue(obj, random.Sub(randomizedField.key)); } foreach (StageRef.ConfigFieldRef configField in stageRef.configFields) { configField.field.SetValue(obj, configs[configField.config.type]); } foreach (StageRef.ResourceFieldRef resourceField in stageRef.resourceFields) { resourceField.field.SetValue(obj, dependencies[resourceField.resourceId].GetValue()); } return obj; } private object ConstructInstanceForLane(RandomDirectory random, int lane, Dictionary configs, Dictionary dependencies) { object obj = stageRef.constructor.Invoke(Array.Empty()); foreach (StageRef.LaneFieldRef laneField in stageRef.laneFields) { laneField.field.SetValue(obj, lane); } foreach (StageRef.RandomFieldRef randomizedField in stageRef.randomizedFields) { randomizedField.field.SetValue(obj, random.Sub($"{randomizedField.key}/{stageRef.forkDimension}/{lane}")); } foreach (StageRef.ConfigFieldRef configField in stageRef.configFields) { object obj2 = configs[configField.config.type]; configField.field.SetValue(obj, configField.isLaneConfig ? ((ILaneConfig)obj2).Get(lane) : obj2); } foreach (StageRef.ResourceFieldRef resourceField in stageRef.resourceFields) { resourceField.field.SetValue(obj, dependencies[resourceField.resourceId].GetValue(lane)); } return obj; } } public interface IGeneratorStage { T Compute(); } public interface ILaneConfig { object Get(int lane); } public interface ILaneConfig : ILaneConfig { new T Get(int lane); } public class GeneratorPipeline { public record PipelineStartEvent { public int seed; } public record TaskStartEvent { public string[] resources; public int progress; } public record TaskCompletionEvent { public string tid; public string[] resources; public TimeSpan duration; public bool cached; public int progress; } public record PipelineCompletionEvent { public TimeSpan duration; } private record StageResult { public static StageResult Cached => new StageResult { tid = null, resources = new Dictionary(), duration = TimeSpan.Zero, cached = true }; public string tid; public Dictionary resources; public TimeSpan duration; public bool cached; public TaskCompletionEvent ToEvent(int progress) { return new TaskCompletionEvent { tid = tid, resources = resources.Keys.ToArray(), duration = duration, cached = cached, progress = progress }; } } public class Builder { private List resultResourceIds; private Dictionary laneCounts = new Dictionary(); private List assemblies = new List(); private bool parallel; private bool clearUnneededCache; public Builder(params string[] resultResourceIds) { this.resultResourceIds = resultResourceIds.ToList(); if (resultResourceIds.Length == 0) { throw new PipelineError("No result resource ids specified"); } } public Builder AddResult(string resourceId) { resultResourceIds.Add(resourceId); return this; } public Builder SetParallel(bool value) { parallel = value; return this; } public Builder SetClearUnneededCache(bool value) { clearUnneededCache = value; return this; } public Builder AddAssembly(Assembly assembly) { assemblies.Add(assembly); return this; } public Builder SetLaneCount(string forkDimension, int laneCount) { if (laneCount <= 0) { throw new PipelineError("Lane count must be greater than 0 for dimension '" + forkDimension + "'"); } laneCounts[forkDimension] = laneCount; return this; } public Builder SetLaneCount(string forkDimension) where TEnum : Enum { return SetLaneCount(forkDimension, Enum.GetValues(typeof(TEnum)).Length); } public GeneratorPipeline Build() { Registry.RegisterAssembly(GetType().Assembly); foreach (Assembly assembly in assemblies) { Registry.RegisterAssembly(assembly); } return new GeneratorPipeline(resultResourceIds.ToArray(), laneCounts) { parallel = parallel, clearUnneededCache = clearUnneededCache }; } } [CompilerGenerated] private sealed class <>c__DisplayClass32_0 { public object config; internal IEnumerable b__0(FieldInfo field) { return UnpackCompositeConfig(field.GetValue(config)); } } [CompilerGenerated] private sealed class d__32 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; private int <>l__initialThreadId; private object config; public object <>3__config; private IEnumerator <>7__wrap1; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__32(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { int num = <>1__state; if (num == -3 || num == 2) { try { } finally { <>m__Finally1(); } } <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { try { switch (<>1__state) { default: return false; case 0: { <>1__state = -1; <>c__DisplayClass32_0 CS$<>8__locals0 = new <>c__DisplayClass32_0 { config = config }; Type type = CS$<>8__locals0.config.GetType(); if (type.GetCustomAttribute() == null) { <>2__current = CS$<>8__locals0.config; <>1__state = 1; return true; } IEnumerable enumerable = type.GetFields(BindingFlags.Instance | BindingFlags.Public).SelectMany((FieldInfo field) => UnpackCompositeConfig(field.GetValue(CS$<>8__locals0.config))); <>7__wrap1 = enumerable.GetEnumerator(); <>1__state = -3; goto IL_00ca; } case 1: <>1__state = -1; break; case 2: { <>1__state = -3; goto IL_00ca; } IL_00ca: if (<>7__wrap1.MoveNext()) { object current = <>7__wrap1.Current; <>2__current = current; <>1__state = 2; return true; } <>m__Finally1(); <>7__wrap1 = null; break; } return false; } catch { //try-fault ((IDisposable)this).Dispose(); throw; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } private void <>m__Finally1() { <>1__state = -1; if (<>7__wrap1 != null) { <>7__wrap1.Dispose(); } } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__32 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__32(0); } d__.config = <>3__config; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } private readonly ComputationGraph graph; private readonly PipelineCache cache; private readonly Dictionary stages; private bool parallel; private bool clearUnneededCache; public int Length => graph.TotalResources; public event Action PipelineStarted; public event Action TaskStarted; public event Action TaskCompleted; public event Action PipelineCompleted; private GeneratorPipeline(string[] resultResourceIds, Dictionary forkingRuleOverrides) { graph = new ComputationGraph(resultResourceIds); cache = new PipelineCache(graph); stages = CreateStages(graph, GetForkingLaneCounts(forkingRuleOverrides)); } private static StageRef GetStageRef(string resourceId) { return Registry.GetResource(resourceId).producer; } private static Dictionary GetForkingLaneCounts(Dictionary forkingRuleOverrides) { Dictionary forkingRules = Registry.GetForkingRules(); foreach (KeyValuePair forkingRuleOverride in forkingRuleOverrides) { forkingRules[forkingRuleOverride.Key] = forkingRuleOverride.Value; } return forkingRules; } private static Dictionary CreateStages(ComputationGraph graph, Dictionary forkingLaneCounts) { Dictionary dictionary = new Dictionary(); foreach (string allResource in graph.GetAllResources()) { StageRef stageRef = GetStageRef(allResource); dictionary[stageRef] = new GeneratorStage(stageRef, forkingLaneCounts); } return dictionary; } public T GetResult(string resourceId) { if (!cache.TryGet(resourceId, out var resource)) { throw new PipelineError("No result with id '" + resourceId + "' in cache"); } return (T)resource; } public void Compute(int seed, object masterConfig) { if (masterConfig.GetType().GetCustomAttribute() == null) { throw new PipelineError("Type '" + masterConfig.GetType().Name + "' is not a composite config"); } Compute(seed, new List { masterConfig }); } public void Compute(int seed, List configs) { this.PipelineStarted?.Invoke(new PipelineStartEvent { seed = seed }); cache.UpdateSeed(seed); cache.UpdateConfigs(GetValidatedConfigs(UnpackAllConfigs(configs))); using (new StopWatch(delegate(TimeSpan span) { this.PipelineCompleted?.Invoke(new PipelineCompletionEvent { duration = span }); })) { if (parallel) { ComputeAllParallel(new RandomDirectory(seed)); } else { ComputeAll(new RandomDirectory(seed)); } } } private static List UnpackAllConfigs(List configs) { return configs.SelectMany(UnpackCompositeConfig).ToList(); } [IteratorStateMachine(typeof(d__32))] private static IEnumerable UnpackCompositeConfig(object config) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__32(-2) { <>3__config = config }; } private Dictionary GetValidatedConfigs(List configs) { Dictionary dictionary = new Dictionary(); foreach (object config in configs) { Type type = config.GetType(); dictionary[type] = config; if (type.TryGetLaneConfigInterface(out var iface)) { dictionary[iface] = config; } } Dictionary dictionary2 = new Dictionary(); foreach (ConfigRef configRef in stages.Keys.SelectMany((StageRef stageRef) => stageRef.Configs).Distinct()) { if (!dictionary.TryGetValue(configRef.type, out var value)) { throw new PipelineError("Missing config '" + configRef.type.Name + "' required by stage '" + stages.Keys.First((StageRef stageRef) => stageRef.Configs.Contains(configRef)).type.Name + "'"); } dictionary2.Add(configRef, value); } return dictionary2; } private void ComputeAll(RandomDirectory random) { HashSet completed = new HashSet(); Queue queue = new Queue(); foreach (StageRef item in graph.GetRoots().Select(GetStageRef).Distinct()) { queue.Enqueue(item); } int num = 0; while (queue.Count > 0) { StageRef stageRef = queue.Dequeue(); if (!completed.Add(stageRef)) { continue; } this.TaskStarted?.Invoke(new TaskStartEvent { resources = stageRef.producerOf.ToArray(), progress = num }); StageResult stageResult = ComputeSingleSync(stageRef, random); foreach (KeyValuePair resource in stageResult.resources) { cache.Put(resource.Key, resource.Value); } num += stageRef.producerOf.Count; this.TaskCompleted?.Invoke(stageResult.ToEvent(num)); foreach (string item2 in stageRef.producerOf) { foreach (string blockedResource in graph.GetBlockedResources(item2)) { if (graph.GetRequiredResources(blockedResource).All((string resourceId) => completed.Contains(GetStageRef(resourceId)))) { StageRef stageRef2 = GetStageRef(blockedResource); if (!completed.Contains(stageRef2)) { queue.Enqueue(stageRef2); } } } } if (!clearUnneededCache) { continue; } foreach (string item3 in stageRef.ConsumerOf) { if (graph.GetBlockedResources(item3).All((string resId) => completed.Contains(GetStageRef(resId)))) { cache.Drop(item3); } } } } private void ComputeAllParallel(RandomDirectory random) { HashSet pending = new HashSet(); foreach (string allResource in graph.GetAllResources()) { pending.Add(GetStageRef(allResource)); } Dictionary> dictionary = new Dictionary>(); foreach (StageRef item in graph.GetRoots().Select(GetStageRef).Distinct()) { dictionary.Add(item, ComputeSingleAsync(item, random)); } int num = 0; while (pending.Count > 0) { KeyValuePair>[] array = dictionary.ToArray(); KeyValuePair> keyValuePair = array[Task.WaitAny(((IEnumerable>>)array).Select((Func>, Task>)((KeyValuePair> pair) => pair.Value)).ToArray())]; StageRef key = keyValuePair.Key; StageResult result = keyValuePair.Value.Result; dictionary.Remove(key); pending.Remove(key); foreach (string key2 in result.resources.Keys) { cache.Put(key2, result.resources[key2]); } num += key.producerOf.Count; this.TaskCompleted?.Invoke(result.ToEvent(num)); foreach (string item2 in key.producerOf) { foreach (string blockedResource in graph.GetBlockedResources(item2)) { if (graph.GetRequiredResources(blockedResource).All((string resourceId) => !pending.Contains(GetStageRef(resourceId)))) { StageRef stageRef = GetStageRef(blockedResource); if (pending.Contains(stageRef) && !dictionary.ContainsKey(stageRef)) { this.TaskStarted?.Invoke(new TaskStartEvent { resources = stageRef.producerOf.ToArray(), progress = num }); dictionary.Add(stageRef, ComputeSingleAsync(stageRef, random)); } } } } if (!clearUnneededCache) { continue; } foreach (string item3 in key.ConsumerOf) { if (graph.GetBlockedResources(item3).All((string resId) => !pending.Contains(GetStageRef(resId)))) { cache.Drop(item3); } } } } private StageResult ComputeSingleSync(StageRef stageRef, RandomDirectory random) { if (stageRef.producerOf.All((string resourceId) => cache.TryGet(resourceId, out var _))) { return StageResult.Cached; } return ComputeSingle(stages[stageRef].GetPackagedTask(random, cache)); } private Task ComputeSingleAsync(StageRef stageRef, RandomDirectory random) { if (stageRef.producerOf.All((string resourceId) => cache.TryGet(resourceId, out var _))) { return Task.FromResult(StageResult.Cached); } Func> packagedTask = stages[stageRef].GetPackagedTask(random, cache); return Task.Run(() => ComputeSingle(packagedTask, Thread.CurrentThread.ManagedThreadId.ToString())); } private static StageResult ComputeSingle(Func> packagedTask, string tid = null) { DateTime now = DateTime.Now; Dictionary resources = packagedTask(); return new StageResult { tid = tid, resources = resources, duration = DateTime.Now - now, cached = false }; } } internal class PipelineCache { private int? seed; private readonly Dictionary configs = new Dictionary(); private readonly Dictionary resources = new Dictionary(); private readonly ComputationGraph graph; public PipelineCache(ComputationGraph graph) { this.graph = graph; } public void UpdateSeed(int newSeed) { if (newSeed != seed) { seed = newSeed; resources.Clear(); } } public void UpdateConfigs(Dictionary newConfigs) { foreach (ConfigRef key in newConfigs.Keys) { if (configs.TryGetValue(key, out var value) && value.Equals(newConfigs[key])) { continue; } configs[key] = CopyConfig(newConfigs[key]); foreach (string item in graph.GetBlockedResourcesRecursive(key)) { resources.Remove(item); } } } public object GetConfig(ConfigRef configRef) { return configs[configRef]; } public void Put(string resourceId, object resource) { resources[resourceId] = resource; } public bool TryGet(string resourceId, out object resource) { return resources.TryGetValue(resourceId, out resource); } public void Drop(string resourceId) { resources.Remove(resourceId); } private static object CopyConfig(object config) { if (config == null) { return null; } Type type = config.GetType(); if (type.IsPrimitive || type == typeof(string)) { return config; } if (!type.IsPrimitive && !type.IsEnum && type.GetCustomAttribute() == null) { throw new PipelineError("Config " + type.FullName + " is not serializable"); } object obj = Activator.CreateInstance(type); FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { object value = fieldInfo.GetValue(config); if (value != null) { fieldInfo.SetValue(obj, CopyConfig(value)); } } return obj; } } internal class PipelineError : Exception { public PipelineError(string msg) : base(msg) { } } internal class StageRef { public struct LaneFieldRef { public FieldInfo field; } public struct RandomFieldRef { public FieldInfo field; public string key; } public struct ConfigFieldRef { public FieldInfo field; public ConfigRef config; public bool isLaneConfig; } public struct ResourceFieldRef { public FieldInfo field; public string resourceId; } public Type type; public Type producedType; public List producerOf; public string forkDimension; public List laneFields; public List randomizedFields; public List resourceFields; public List configFields; public ConstructorInfo constructor; public MethodInfo computeMethod; public bool IsForked => forkDimension != null; public List ConsumerOf => resourceFields.Select((ResourceFieldRef fieldRef) => fieldRef.resourceId).ToList(); public List Configs => configFields.Select((ConfigFieldRef fieldRef) => fieldRef.config).ToList(); } internal struct ResourceRef : IEquatable { public Type type; public string name; public StageRef producer; public bool IsForked => producer.IsForked; public string ForkDimension => producer.forkDimension; public bool Equals(ResourceRef other) { if (type == other.type && name == other.name) { return producer.Equals(other.producer); } return false; } public override bool Equals(object obj) { if (obj is ResourceRef other) { return Equals(other); } return false; } public override int GetHashCode() { return HashCode.Combine(name, type, producer); } } internal struct ConfigRef : IEquatable { public Type type; public bool Equals(ConfigRef other) { return type == other.type; } public override bool Equals(object obj) { if (obj is ConfigRef other) { return Equals(other); } return false; } public override int GetHashCode() { return type.GetHashCode(); } } internal static class Registry { [CompilerGenerated] private sealed class d__13 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private StageRef.ConfigFieldRef <>2__current; private int <>l__initialThreadId; private Type type; public Type <>3__type; private bool forkedCompute; public bool <>3__forkedCompute; private FieldInfo[] <>7__wrap1; private int <>7__wrap2; private FieldInfo 5__4; private bool 5__5; StageRef.ConfigFieldRef IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__13(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>7__wrap1 = null; 5__4 = null; <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>7__wrap1 = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); <>7__wrap2 = 0; goto IL_01ae; case 1: <>1__state = -1; goto IL_00f7; case 2: { <>1__state = -1; goto IL_0199; } IL_01ae: if (<>7__wrap2 < <>7__wrap1.Length) { 5__4 = <>7__wrap1[<>7__wrap2]; bool num = 5__4.GetCustomAttribute() != null; 5__5 = 5__4.GetCustomAttribute() != null; if (num & 5__5) { throw new PipelineError($"Field '{5__4.Name}' of type '{type}' is marked with both 'Config' and 'LaneConfig' attributes"); } if (num) { <>2__current = new StageRef.ConfigFieldRef { field = 5__4, config = new ConfigRef { type = 5__4.FieldType } }; <>1__state = 1; return true; } goto IL_00f7; } <>7__wrap1 = null; return false; IL_0199: 5__4 = null; <>7__wrap2++; goto IL_01ae; IL_00f7: if (5__5) { if (!forkedCompute) { throw new PipelineError($"Field '{5__4.Name}' of type '{type}': can't have 'LaneConfig' attribute unless it's a forked stage"); } <>2__current = new StageRef.ConfigFieldRef { field = 5__4, config = new ConfigRef { type = typeof(ILaneConfig<>).MakeGenericType(5__4.FieldType) }, isLaneConfig = true }; <>1__state = 2; return true; } goto IL_0199; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__13 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__13(0); } d__.type = <>3__type; d__.forkedCompute = <>3__forkedCompute; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [CompilerGenerated] private sealed class d__11 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private StageRef.LaneFieldRef <>2__current; private int <>l__initialThreadId; private Type type; public Type <>3__type; private bool forkedCompute; public bool <>3__forkedCompute; private FieldInfo[] <>7__wrap1; private int <>7__wrap2; StageRef.LaneFieldRef IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__11(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_00c3; } <>1__state = -1; <>7__wrap1 = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); <>7__wrap2 = 0; goto IL_00d1; IL_00c3: <>7__wrap2++; goto IL_00d1; IL_00d1: if (<>7__wrap2 < <>7__wrap1.Length) { FieldInfo fieldInfo = <>7__wrap1[<>7__wrap2]; if (fieldInfo.GetCustomAttribute() != null) { if (!forkedCompute) { throw new PipelineError($"Field '{fieldInfo.Name}' of type '{type}' is marked with 'Lane' attribute, but it's not a forked stage"); } if (!fieldInfo.FieldType.IsIntegral()) { throw new PipelineError($"Field '{fieldInfo.Name}' of type '{type}' is marked with 'Lane' attribute, but it's not an integral type"); } <>2__current = new StageRef.LaneFieldRef { field = fieldInfo }; <>1__state = 1; return true; } goto IL_00c3; } <>7__wrap1 = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__11 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__11(0); } d__.type = <>3__type; d__.forkedCompute = <>3__forkedCompute; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [CompilerGenerated] private sealed class d__12 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private StageRef.RandomFieldRef <>2__current; private int <>l__initialThreadId; private Type type; public Type <>3__type; private FieldInfo[] <>7__wrap1; private int <>7__wrap2; StageRef.RandomFieldRef IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__12(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_00b8; } <>1__state = -1; <>7__wrap1 = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); <>7__wrap2 = 0; goto IL_00c6; IL_00b8: <>7__wrap2++; goto IL_00c6; IL_00c6: if (<>7__wrap2 < <>7__wrap1.Length) { FieldInfo fieldInfo = <>7__wrap1[<>7__wrap2]; RandomAttribute customAttribute = fieldInfo.GetCustomAttribute(); if (customAttribute != null) { if (fieldInfo.FieldType != typeof(RandomDirectory)) { throw new PipelineError($"Field '{fieldInfo.Name}' of type '{type}' is marked with 'Random' attribute, but it's type is not 'RandomDirectory'"); } <>2__current = new StageRef.RandomFieldRef { field = fieldInfo, key = customAttribute.key }; <>1__state = 1; return true; } goto IL_00b8; } <>7__wrap1 = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__12 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__12(0); } d__.type = <>3__type; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } [CompilerGenerated] private sealed class d__14 : IEnumerable, IEnumerable, IEnumerator, IEnumerator, IDisposable { private int <>1__state; private StageRef.ResourceFieldRef <>2__current; private int <>l__initialThreadId; private Type type; public Type <>3__type; private FieldInfo[] <>7__wrap1; private int <>7__wrap2; StageRef.ResourceFieldRef IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__14(int <>1__state) { this.<>1__state = <>1__state; <>l__initialThreadId = Environment.CurrentManagedThreadId; } [DebuggerHidden] void IDisposable.Dispose() { <>7__wrap1 = null; <>1__state = -2; } private bool MoveNext() { int num = <>1__state; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; goto IL_007f; } <>1__state = -1; <>7__wrap1 = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); <>7__wrap2 = 0; goto IL_008d; IL_007f: <>7__wrap2++; goto IL_008d; IL_008d: if (<>7__wrap2 < <>7__wrap1.Length) { FieldInfo fieldInfo = <>7__wrap1[<>7__wrap2]; ResourceAttribute customAttribute = fieldInfo.GetCustomAttribute(); if (customAttribute != null) { <>2__current = new StageRef.ResourceFieldRef { field = fieldInfo, resourceId = customAttribute.resourceId }; <>1__state = 1; return true; } goto IL_007f; } <>7__wrap1 = null; return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { d__14 d__; if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId) { <>1__state = 0; d__ = this; } else { d__ = new d__14(0); } d__.type = <>3__type; return d__; } [DebuggerHidden] IEnumerator IEnumerable.GetEnumerator() { return ((IEnumerable)this).GetEnumerator(); } } private static readonly HashSet RegisteredAssemblies = new HashSet(); private static readonly Dictionary Resources = new Dictionary(); private static readonly Dictionary ForkingRules = new Dictionary(); private static readonly object RegistryLock = new object(); public static void RegisterAssembly(Assembly assembly) { lock (RegistryLock) { if (RegisteredAssemblies.Add(assembly)) { RegisterAssemblyImpl(assembly); } } } public static ResourceRef GetResource(string resourceName) { lock (RegistryLock) { if (!Resources.TryGetValue(resourceName, out var value)) { throw new PipelineError("No known producer for '" + resourceName + "'"); } return value; } } public static Dictionary GetAllResourcesFor(params string[] resourceNames) { lock (RegistryLock) { Dictionary dictionary = new Dictionary(); Queue<(string, string)> queue = new Queue<(string, string)>(); foreach (string item in resourceNames) { queue.Enqueue((item, "ROOT")); } while (queue.Count > 0) { var (text, text2) = queue.Dequeue(); if (!Resources.TryGetValue(text, out var value)) { throw new PipelineError("No known producer for '" + text + "', requested by '" + text2 + "'"); } foreach (string item2 in value.producer.producerOf) { dictionary[item2] = value; } foreach (string item3 in value.producer.ConsumerOf) { if (!dictionary.ContainsKey(item3)) { queue.Enqueue((item3, value.producer.type.Name)); } } } return dictionary; } } public static Dictionary GetForkingRules() { return ForkingRules.ToDictionary((KeyValuePair kv) => kv.Key, (KeyValuePair kv) => kv.Value); } private static void RegisterAssemblyImpl(Assembly assembly) { Type[] types = assembly.GetTypes(); foreach (Type type in types) { if (type.TryGetGeneratorStageInterface(out var iface)) { RegisterGeneratorStage(type, iface.GetGenericArguments()); } ForkingDimensionAttribute customAttribute = type.GetCustomAttribute(); if (customAttribute != null) { RegisterForkingRule(customAttribute); } } } private static void RegisterGeneratorStage(Type type, Type[] args) { ProducerAttribute customAttribute = type.GetCustomAttribute(); if (customAttribute == null) { throw new PipelineError("Stage '" + type.Name + "' is missing 'Producer' attribute"); } List list = new List(); if (customAttribute.resourceIds.Length == 1) { list.Add(args[0]); } else { Type type2 = args[0]; if (!type2.IsTuple()) { throw new PipelineError($"Stage '{type.Name}' declares two or more outputs, but it's output type {type2} is not a tuple"); } list.AddRange(type2.GetGenericArguments()); } bool forkedCompute = false; ForkAttribute? customAttribute2 = type.GetCustomAttribute(); if (customAttribute2 != null) { forkedCompute = true; } string forkDimension = customAttribute2?.forkDimension; StageRef stageRef = new StageRef { type = type, producedType = args[0], producerOf = customAttribute.resourceIds.ToList(), forkDimension = forkDimension, laneFields = GetLaneFields(type, forkedCompute).ToList(), randomizedFields = GetRandomizedFields(type).ToList(), resourceFields = GetResourceFields(type).ToList(), configFields = GetConfigFields(type, forkedCompute).ToList(), constructor = type.GetConstructor(Type.EmptyTypes), computeMethod = type.GetMethod("Compute", Type.EmptyTypes) }; for (int i = 0; i < stageRef.producerOf.Count; i++) { ResourceRef resourceRef = default(ResourceRef); resourceRef.type = list[i]; resourceRef.name = stageRef.producerOf[i]; resourceRef.producer = stageRef; ResourceRef value = resourceRef; if (Resources.TryGetValue(value.name, out var value2)) { throw new PipelineError($"Multiple producers for '{value.name}': '{value.producer.type}' and '{value2.producer.type}'"); } Resources[value.name] = value; } } private static void RegisterForkingRule(ForkingDimensionAttribute attr) { if (ForkingRules.ContainsKey(attr.dimension)) { throw new PipelineError("Ambiguous forking rule for dimension '" + attr.dimension + "'"); } ForkingRules[attr.dimension] = attr.laneCount; } [IteratorStateMachine(typeof(d__11))] private static IEnumerable GetLaneFields(Type type, bool forkedCompute) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__11(-2) { <>3__type = type, <>3__forkedCompute = forkedCompute }; } [IteratorStateMachine(typeof(d__12))] private static IEnumerable GetRandomizedFields(Type type) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__12(-2) { <>3__type = type }; } [IteratorStateMachine(typeof(d__13))] private static IEnumerable GetConfigFields(Type type, bool forkedCompute) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__13(-2) { <>3__type = type, <>3__forkedCompute = forkedCompute }; } [IteratorStateMachine(typeof(d__14))] private static IEnumerable GetResourceFields(Type type) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__14(-2) { <>3__type = type }; } } } namespace Riverheim.Pipeline.Extensions { internal static class TypeExtensions { public static bool TryGetGeneratorStageInterface(this Type type, out Type iface) { iface = type.GetInterfaces().FirstOrDefault((Type i) => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IGeneratorStage<>)); return iface != null; } public static bool TryGetLaneConfigInterface(this Type type, out Type iface) { iface = type.GetInterfaces().FirstOrDefault((Type i) => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ILaneConfig<>)); return iface != null; } public static bool IsTuple(this Type type) { if (type == null) { return false; } if (type.IsGenericType) { string text = type.GetGenericTypeDefinition()?.FullName; if (text != null) { if (!text.StartsWith("System.ValueTuple`")) { return text.StartsWith("System.Tuple`"); } return true; } } return false; } public static bool IsIntegral(this Type type) { if (type.IsEnum) { return true; } TypeCode typeCode = Type.GetTypeCode(type); if ((uint)(typeCode - 4) <= 8u) { return true; } return false; } } } namespace Riverheim.Configuration { public static class ConfigManager { public const string PRESET_DEFAULT = "builtin.amur"; public const string PRESET_EXPERIMENTAL = "builtin.experimental"; public const int PRESET_DEFAULT_VERSION = 0; private static readonly Dictionary PresetHandlers; public static WorldSettings DefaultWorldSettings() { return new WorldSettings("builtin.amur", 0, Array.Empty(), Array.Empty()); } public static bool RegisterPresetHandler(string presetName, IPresetHandler presetHandler) { if (PresetHandlers.ContainsKey(presetName)) { return false; } if (presetHandler.ParentPreset() != "builtin.amur") { throw new NotImplementedException("Inheriting non-master presets not supported at this time"); } PresetHandlers[presetName] = presetHandler; return true; } public static bool UnregisterPresetHandler(string presetName, IPresetHandler presetHandler) { if (PresetHandlers.TryGetValue(presetName, out var value) && presetHandler == value) { PresetHandlers.Remove(presetName); return true; } return false; } static ConfigManager() { PresetHandlers = new Dictionary(); RegisterPresetHandler("builtin.experimental", new ExperimentalPresetHandler(0)); } private static WorldGenerationConfig GetMasterConfig() { WorldGenerationConfig result = default(WorldGenerationConfig); result.common = DefaultConfig.CommonConfig(); result.world = DefaultConfig.Config(); result.rendering = DefaultConfig.RenderingConfig(); return result; } public static WorldGenerationConfig GetConfig(WorldSettings settings) { if (settings.Preset == "builtin.amur") { return GetMasterConfig(); } if (PresetHandlers.TryGetValue(settings.Preset, out var value)) { return value.GetConfig(settings.PresetVersion, GetMasterConfig()); } throw new ArgumentOutOfRangeException(); } public static bool Validate(WorldSettings settings, out string message) { message = ""; if (!ValidatePreset(settings.Preset, settings.PresetVersion, ref message)) { return false; } WorldSettings.Feature[] features = settings.Features; for (int i = 0; i < features.Length; i++) { if (!ValidateFeature(features[i], ref message)) { return false; } } WorldSettings.Parameter[] parameters = settings.Parameters; for (int i = 0; i < parameters.Length; i++) { if (!ValidateParameter(parameters[i], ref message)) { return false; } } return true; } private static bool ValidatePreset(string preset, int presetVersion, ref string message) { if (preset == "builtin.amur") { if (presetVersion != 0) { message = $"Unsupported preset version '{presetVersion}' for preset '{preset}'"; return false; } return true; } if (PresetHandlers.TryGetValue(preset, out var value)) { if (presetVersion > value.MaxSupportedVersion()) { message = $"Unsupported preset version '{presetVersion}' for preset '{preset}'"; return false; } return true; } message = "Unknown preset '" + preset + "'"; return false; } private static bool ValidateFeature(WorldSettings.Feature feature, ref string message) { message = "Unknown feature '" + feature.Name + "'"; return false; } private static bool ValidateParameter(WorldSettings.Parameter parameter, ref string message) { message = "Unknown parameter '" + parameter.Name + "'"; return false; } } internal static class DefaultConfig { private static BiomeConfig Biomes { get { BiomeConfig biomeConfig = new BiomeConfig(); biomeConfig.conditionalPlacement = new RegionalConfig { main = new ConditionalBiomePlacementStage.Config { mountainCriterion = ConditionalBiomePlacementStage.Config.MountainCriterion.Scoring, oceanDepth = -20.0, oceanCoastalProximity = 130.0, mountainScoreThreshold = 0.85, mountainScore = new MountainUtility.MountainScoringConfig { headroom = 10.0, kSpine = -15.0, kValley = 10.0, kSaddle = -10.0, kHeight = 0.5, kOvershoot = 3.0 }, mountainScoreTemp = 10.0 }, northPole = new ConditionalBiomePlacementStage.Config { oceanDepth = -20.0, oceanCoastalProximity = 130.0 }, southPole = new ConditionalBiomePlacementStage.Config { oceanDepth = -20.0, oceanCoastalProximity = 130.0 } }; biomeConfig.affinity = new RegionalConfig> { main = new AllBiomeConfig { meadows = new BiomeAffinity.Config { travelDistance = new StaticArray((180.0, 100.0), (250.0, 50.0), (500.0, 0.0)), relativeTravelDistance = new StaticArray((0.0, 0.0), (0.5, -40.0)), riverDistance = new StaticArray((0.0, 5.0), (140.0, 0.0)), height = new StaticArray((30.0, 0.0), (40.0, -10.0)), roughness = new StaticArray((0.0, 0.0), (1.0, -5.0)) }, marsh = new BiomeAffinity.Config { relativeTravelDistance = new StaticArray((0.0, -30.0), (0.05, 0.0), (0.5, -40.0)), coastalDistance = new StaticArray((0.0, -80.0), (140.0, -40.0), (200.0, 0.0)), height = new StaticArray((-10.0, -100.0), (2.0, 0.0), (8.0, 0.0), (40.0, -100.0)), roughness = new StaticArray((0.0, 0.0), (1.0, -20.0)), riverSourceBias = 10.0 }, forest = new BiomeAffinity.Config { bias = -5.0 }, swamp = new BiomeAffinity.Config { bias = 8.0, relativeTravelDistance = new StaticArray((0.0, -1000.0), (0.18, -100.0), (0.2, 0.0), (0.58, 0.0), (0.6, -100.0)), coastalDistance = new StaticArray((0.0, -90.0), (300.0, 0.0)), riverDistance = new StaticArray((0.0, -90.0), (150.0, -50.0), (230.0, 10.0), (400.0, 0.0)), height = new StaticArray((-20.0, -100.0), (-5.0, 60.0), (5.0, 60.0), (20.0, -100.0)), roughness = new StaticArray((0.0, 0.0), (0.5, -20.0)) }, plains = new BiomeAffinity.Config { bias = 5.0, relativeTravelDistance = new StaticArray((0.0, -1000.0), (0.27, -100.0), (0.29, 0.0), (0.66, 0.0), (0.68, -40.0)), height = new StaticArray((30.0, 0.0), (40.0, -5.0)) }, mistlands = new BiomeAffinity.Config { bias = 20.0, relativeTravelDistance = new StaticArray((0.0, -2000.0), (0.55, -100.0), (0.6, 0.0)), coastalDistance = new StaticArray((0.0, 10.0), (200.0, 0.0)), angle = new StaticArray((-90.0, 30.0), (0.0, -30.0), (90.0, 30.0)) }, mistglade = new BiomeAffinity.Config { coastalDistance = new StaticArray((0.0, 10.0), (200.0, 0.0)), height = new StaticArray((-40.0, -100.0), (0.0, 0.0), (12.0, 0.0), (100.0, -100.0)), riverDistance = new StaticArray((0.0, 15.0), (200.0, 0.0)) } }, southPole = new AllBiomeConfig { lavaplain = new BiomeAffinity.Config { noise = new BiomeAffinity.NoiseConfig { scale = 500.0, magnitude = 5.0 }, coastalDistance = new StaticArray((0.0, -50.0), (120.0, -12.0), (300.0, 0.0)), height = new StaticArray((0.0, -100.0), (6.0, -10.0), (10.0, 0.0)), relativeTravelDistance = new StaticArray((0.0, -1000.0), (0.85, 0.0), (1.15, 20.0)), radialDistance = new StaticArray((0.0, -1000.0), (0.85, 0.0), (1.0, 20.0)), angle = new StaticArray((-45.0, 0.0), (-20.0, 40.0), (20.0, 40.0), (45.0, 0.0)) } } }; biomeConfig.competitiveScoring = new RegionalConfig { main = new CompetitiveBiomeScoreStage.Config { normalization = new BiomeAffinityNormalization.Config { temperature = 20.0, multiplier = 100.0 }, randomness = 5.0, originSpacing = 200.0 }, southPole = new CompetitiveBiomeScoreStage.Config { normalization = new BiomeAffinityNormalization.Config { temperature = 20.0, multiplier = 100.0 }, randomness = 5.0, originSpacing = 200.0 }, northPole = new CompetitiveBiomeScoreStage.Config { normalization = new BiomeAffinityNormalization.Config { temperature = 20.0, multiplier = 100.0 }, randomness = 5.0, originSpacing = 200.0 } }; biomeConfig.competitivePlacement = new RegionalConfig { main = new CompetitiveBiomePlacementStage.Config { limit = 0.95, overfillNeighborBonus = 0.5, selectionMethod = CompetitiveBiomePlacementStage.Config.SelectionMethod.Proportional, biomes = new AllBiomeConfig { meadows = new BiomePlacementConfig { weight = 69.5, scoring = new SingleBiomePlacement.Config { cohesionBonusMul = 0.1, targetPatchSize = 200.0 } }, marsh = new BiomePlacementConfig { weight = 5.5, scoring = new SingleBiomePlacement.Config { cohesionBonusMul = 0.5, targetPatchSize = 65.0 } }, forest = new BiomePlacementConfig { weight = 261.0, scoring = new SingleBiomePlacement.Config { cohesionBonusMul = 0.05, targetPatchSize = 230.0 } }, swamp = new BiomePlacementConfig { weight = 76.0, scoring = new SingleBiomePlacement.Config { cohesionBonusAdd = 6.5, cohesionBonusMul = 1.5, targetPatchSize = 570.0, patchSizeTemp = 0.1 } }, plains = new BiomePlacementConfig { weight = 283.0, scoring = new SingleBiomePlacement.Config { cohesionBonusMul = 0.1, targetPatchSize = 650.0 } }, mistlands = new BiomePlacementConfig { weight = 305.0, scoring = new SingleBiomePlacement.Config { cohesionBonusMul = 0.1, targetPatchSize = 210.0 } } } }, northPole = new CompetitiveBiomePlacementStage.Config { limit = 0.95, selectionMethod = CompetitiveBiomePlacementStage.Config.SelectionMethod.Proportional, biomes = new AllBiomeConfig { deepNorth = new BiomePlacementConfig { weight = 1000.0, scoring = new SingleBiomePlacement.Config { cohesionBonusMul = 0.1, targetPatchSize = 500.0 } } } }, southPole = new CompetitiveBiomePlacementStage.Config { limit = 0.95, selectionMethod = CompetitiveBiomePlacementStage.Config.SelectionMethod.Proportional, biomes = new AllBiomeConfig { ashlands = new BiomePlacementConfig { weight = 1000.0, scoring = new SingleBiomePlacement.Config { cohesionBonusMul = 0.1, targetPatchSize = 500.0 } } } } }; biomeConfig.replacementScoring = new RegionalConfig { main = new BiomeReplacementScoreStage.Config { affinity = new AllBiomeConfig { mistglade = new BiomeReplacementAffinityConfig { boundaryProximity = new StaticArray((50.0, -100.0), (200.0, 0.0)), patchRadius = new StaticArray((250.0, -100.0), (500.0, 0.0)) } }, normalization = new BiomeAffinityNormalization.Config { temperature = 20.0, multiplier = 100.0 }, originSpacing = 270.0, randomness = 1.0 }, southPole = new BiomeReplacementScoreStage.Config { affinity = new AllBiomeConfig { lavaplain = new BiomeReplacementAffinityConfig { patchRadius = new StaticArray((500.0, -50.0), (3500.0, 0.0)) } }, normalization = new BiomeAffinityNormalization.Config { temperature = 20.0, multiplier = 100.0 }, originSpacing = 270.0, randomness = 1.0 } }; biomeConfig.replacement = new RegionalConfig { main = new BiomeReplacementStage.Config { biomes = new AllBiomeConfig { mistglade = new BiomeReplacementConfig { source = Biome.Mistlands, rate = 0.03, placement = new SingleBiomePlacement.Config { cohesionBonusAdd = 30.0, targetPatchSize = 117.0 } } } }, southPole = new BiomeReplacementStage.Config { biomes = new AllBiomeConfig { lavaplain = new BiomeReplacementConfig { source = Biome.Ashlands, rate = 0.45, placement = new SingleBiomePlacement.Config { cohesionBonusMul = 0.25, targetPatchSize = 300.0 } } } } }; biomeConfig.postprocessing = new RegionalConfig { main = new BiomePostprocessingConfig { meadowsShapingOffset = 1.0, meadowsShapingIntensity = 0.75, meadowsForestShapingIntensity = 0.25, meadowsForestThreshold = 0.475, marshUnderwaterLerpingRangeMin = -15.0, marshUnderwaterLerpingRangeMax = -2.0, marshBaseHeight = -0.25, swampUnderwaterLerpingRangeMin = -15.0, swampUnderwaterLerpingRangeMax = -2.0, swampBaseHeight = -0.25, plainsShapingIntensity = 0.5, plainsShapingOffset = 0.65, plainSmoothingHeightThreshold = 0.0, plainSmoothingRange = 0.0 }, southPole = new BiomePostprocessingConfig { ashlandsHeightCeiling = 19.0, ashlandsHeightSmoothing = 3.0 } }; return biomeConfig; } } private static HeightConfig Height => new HeightConfig { roughnessAnalysis = new RoughnessAnalyzer.Config { flat = new RoughnessAnalyzer.LinearConfig { c = 4.0, pVar = -15.0 }, saddle = new RoughnessAnalyzer.LinearConfig { c = -3.0, pVar = 50.0, pGrad = -8.0, pAbsLapl = -70.0 }, spine = new RoughnessAnalyzer.LinearConfig { c = -5.0, pVar = 12.0, pGrad = -3.0, pLapl = -30.0 }, valley = new RoughnessAnalyzer.LinearConfig { c = -5.0, pVar = 12.0, pGrad = -3.0, pLapl = 30.0 } }, flatlands = new RegionalConfig { main = new FlatlandsStage.Config { noiseScale = 1200.0, noiseFractal = new NoiseField.FractalConfig { type = NoiseField.FractalConfig.FractalType.Max, octaves = 3, lacunarity = 1.3, gain = 0.9, smoothing = 0.0 }, warp = new VectorNoiseField.RelativeWarpConfig { frequency = 5.0, intensity = 0.2 }, noiseThreshold = 0.5 }, northPole = new FlatlandsStage.Config { noiseScale = 1100.0, noiseFractal = new NoiseField.FractalConfig { octaves = 1 }, warp = new VectorNoiseField.RelativeWarpConfig { frequency = 5.0, intensity = 0.4 }, noiseThreshold = 0.38 } }, lakes = new RegionalConfig { main = new LakeStage.Config { budget = 0.004, affinity = new LakeStage.AffinityConfig { noiseScale = 600.0, kNoise = 1.0, kFlatland = 0.35, kCuriosity = 0.8 }, minCoastalProximity = 200.0, expansionNoiseScale = 250.0, expansionVariability = 0.7, expansion = 50.0 } }, boundary = new RegionalConfig { main = new DifferentialHeightBoundaryStage.Config { startingArea = 40.0, fault = 20.0, river = new Rmath.RemapConfig { fromA = 50.0, fromB = 8.0, toA = 3.0, toB = 30.0 }, coastalNoiseScale = 720.0, coastalLandHeight = new Rmath.RemapConfig { From = new Rmath.Range { min = 0.5, max = 0.75 }, To = new Rmath.Range { min = 0.0, max = 38.0 } }, coastalWaterDepth = new Rmath.RemapConfig { From = new Rmath.Range { min = 0.5, max = 0.85 }, To = new Rmath.Range { min = 0.0, max = -25.0 } }, riverDeltaDepth = -10.0 }, northPole = new DifferentialHeightBoundaryStage.Config { fault = 20.0 }, southPole = new DifferentialHeightBoundaryStage.Config { fault = 1.0, coastalNoiseScale = 1000.0, coastalLandHeight = new Rmath.RemapConfig { From = new Rmath.Range { min = 0.6, max = 0.8 }, To = new Rmath.Range { min = 0.0, max = 12.0 } } } }, incline = new RegionalConfig { main = new DifferentialHeightStage.Config { startingAreaBonus = -0.35, land = new DifferentialInclineCalculator.Config { mult = 0.72, relativeNoiseScale = 300.0, relativeNoisePower = 0.1, absoluteNoiseScale = 200.0, absoluteNoiseMagnitude = 0.2, rivers = new DifferentialInclineCalculator.RiverConfig { method = DifferentialInclineCalculator.RiverConfig.Method.GeoMean, scale = 200.0, power = 2.4, p2 = 0.275 }, flats = new DifferentialInclineCalculator.FlatConfig { noiseScale = 1800.0, threshold = 0.0, multiplier = 0.2 }, wind = new DifferentialInclineCalculator.WindConfig { noiseScale = 2500.0, noiseCurve = new StaticArray { new Rpoint2 { x = 0.5, y = 0.0 }, new Rpoint2 { x = 0.6, y = 0.5 }, new Rpoint2 { x = 0.8, y = 0.5 }, new Rpoint2 { x = 0.9, y = 0.8 } } }, penalties = new DifferentialInclineCalculator.PenaltyConfig { partition = 3.0, barrierScale = 500.0, barrierMargin = 0.15, barrierPenalty = 2.2, riverConnection = -0.3, riverAdjacency = -0.15 } }, water = new DifferentialInclineCalculator.Config { mult = 2.0, relativeNoiseScale = 300.0, relativeNoisePower = 0.5, penalties = new DifferentialInclineCalculator.PenaltyConfig { barrierScale = 500.0, barrierMargin = 0.15, barrierPenalty = 1.0 } } }, southPole = new DifferentialHeightStage.Config { land = new DifferentialInclineCalculator.Config { mult = 1.0, flats = new DifferentialInclineCalculator.FlatConfig { noiseScale = 400.0, threshold = 0.25, multiplier = 0.1 }, wind = new DifferentialInclineCalculator.WindConfig { radialStrength = -0.6 }, penalties = new DifferentialInclineCalculator.PenaltyConfig { partition = 2.0, barrierScale = 600.0, barrierMargin = 0.3, barrierPenalty = 2.0 } }, water = new DifferentialInclineCalculator.Config { mult = 2.0, absoluteNoiseScale = 300.0, absoluteNoiseMagnitude = 0.8, penalties = new DifferentialInclineCalculator.PenaltyConfig { barrierScale = 500.0, barrierMargin = 0.15, barrierPenalty = 1.0 } } }, northPole = new DifferentialHeightStage.Config { land = new DifferentialInclineCalculator.Config { mult = 0.7, relativeNoiseScale = 500.0, relativeNoisePower = 1.2, wind = new DifferentialInclineCalculator.WindConfig { noiseScale = 2500.0, noiseCurve = new StaticArray { new Rpoint2 { x = 0.0, y = 0.4 }, new Rpoint2 { x = 0.8, y = 0.75 } } } }, water = new DifferentialInclineCalculator.Config { mult = 2.0, relativeNoiseScale = 300.0, relativeNoisePower = 0.5, penalties = new DifferentialInclineCalculator.PenaltyConfig { barrierScale = 500.0, barrierMargin = 0.15, barrierPenalty = 1.0 } } } }, shaping = new RegionalConfig { main = new HeightShapingStage.Config { land = new HeightShapingStage.ProfileConfig { type = HeightShapingStage.ProfileConfig.ProfileType.SmoothRamp, smoothRamp = new ProfileUtility.SmoothRamp.Config { slope = 1.0, reach = 40.0, power = 3.0 }, bias = 1.0 }, water = new HeightShapingStage.ProfileConfig { type = HeightShapingStage.ProfileConfig.ProfileType.Saturated, saturated = new ProfileUtility.Saturated.Config { pow = 2.4, limit = -40.0, reach = 130.0 } } }, southPole = new HeightShapingStage.Config { land = new HeightShapingStage.ProfileConfig { type = HeightShapingStage.ProfileConfig.ProfileType.Saturated, saturated = new ProfileUtility.Saturated.Config { pow = 2.0, limit = 45.0, reach = 100.0 } }, water = new HeightShapingStage.ProfileConfig { type = HeightShapingStage.ProfileConfig.ProfileType.Saturated, saturated = new ProfileUtility.Saturated.Config { pow = 1.0, limit = -25.0, reach = 90.0 } } }, northPole = new HeightShapingStage.Config { land = new HeightShapingStage.ProfileConfig { type = HeightShapingStage.ProfileConfig.ProfileType.SmoothRamp, smoothRamp = new ProfileUtility.SmoothRamp.Config { slope = 1.0, reach = 10.0, power = 3.0 } }, water = new HeightShapingStage.ProfileConfig { type = HeightShapingStage.ProfileConfig.ProfileType.Saturated, saturated = new ProfileUtility.Saturated.Config { pow = 1.15, limit = -40.0, reach = 240.0 } } } }, mountainEdgeProximity = new RegionalConfig { main = new MountainEdgeProximityStage.Config { trim = 20.0, trimNoiseMagnitude = 25.0, trimNoiseScale = 250.0 } }, mountainQuality = new RegionalConfig { main = new MountainQualityStage.Config { partitionScoring = new MountainQualityStage.PartitionScoringConfig { trimmed = -50.0, area = 0.02, span = 0.02, neighbors = 7.0 }, heightScoringMultiplier = 0.15, localScoreMultiplier = 1.0, roughnessScoring = new MountainUtility.RoughnessScoreConfig { flatnessScale = 5.0, peakinessMidpoint = 0.75, peakinessStrength = 4.0, kFlatness = -10.0, kPeakiness = 0.0, kSaddle = -30.0 }, distanceScoreMultiplier = -0.002, temperature = 4.0, useNewScore = true, localScoring = new MountainUtility.MountainScoringConfig { k0 = -27.0, kFlat = -4.0, kSpine = -2.0, kValley = 3.0, kSaddle = -4.0, kHeight = 0.15 } } }, settlement = new RegionalConfig { main = new HeightSettlementStage.Config { hydraulicErosionRate = 0.12, mountainCutdownRatio = 0.95, mountainMinPatchSize = 180.0, erosionTemperature = 10.0, settlementTemperature = 0.3, hydraulicErosion = new DropletErosionSimulator.Config { bedrockLevel = 8.0, sedimentCapacity = 25.0, erosionRate = 0.1, evaporationRate = 0.02, slopePower = 1.5 }, impactErosion = new ImpactErosionSimulator.Config { rate = 15.0, reach = 320.0 }, polarTrenchWidth = 220.0 }, northPole = new HeightSettlementStage.Config { hydraulicErosionRate = 0.4, erosionTemperature = 10.0, hydraulicErosion = new DropletErosionSimulator.Config { bedrockLevel = 8.0, sedimentCapacity = 25.0, erosionRate = 0.1, evaporationRate = 0.02, slopePower = 1.5 }, polarTrenchWidth = 220.0 }, southPole = new HeightSettlementStage.Config { polarTrenchWidth = 220.0 } } }; private static LandmassConfig Landmass => new LandmassConfig { restrictions = new RegionalConfig { main = new LandmassRestrictionConfig { worldEdgeWidth = 100.0, polarGapWidth = 120.0, reach = 100000.0, swirl = new SwirlConfig { rays = 8, exponent = 1.4, startRadius = 5800.0, noiseScale = 2000.0, noiseOffset = 0.54, toleranceMult = 0.017 }, harbor = new HarborConfig { offset = 150.0, radius = 1700.0, circularity = 0.045 }, featureWarp = new VectorNoiseField.AbsoluteWarpConfig { period = 700.0, intensity = 0.35 } }, southPole = new LandmassRestrictionConfig { worldEdgeWidth = 100.0, polarGapWidth = 350.0, reach = 8500.0, featureWarp = new VectorNoiseField.AbsoluteWarpConfig { period = 700.0, intensity = 0.35 } }, northPole = new LandmassRestrictionConfig { worldEdgeWidth = 100.0, polarGapWidth = 150.0, reach = 100000.0, featureWarp = new VectorNoiseField.AbsoluteWarpConfig { period = 700.0, intensity = 0.35 } } }, painter = new RegionalConfig { main = new LandmassPainterConfig { temperature = 1.0, layerWarp = new VectorNoiseField.RelativeWarpConfig { frequency = 1.0, intensity = 0.64 }, layerPainter = new LayerPainter.Config { distanceWeighting = 0.5, cohesionWeighting = 0.4, layers = new StaticArray { new LandmassLayerConfig { budget = 0.05, greediness = 0.2, variance = 0.3 }, new LandmassLayerConfig { budget = 0.065, greediness = 0.4, variance = 0.3 }, new LandmassLayerConfig { budget = 0.08, greediness = 0.5, variance = 0.3 }, new LandmassLayerConfig { budget = 0.11, greediness = 0.6, variance = 0.25 }, new LandmassLayerConfig { budget = 0.12, greediness = 0.8, variance = 0.2 }, new LandmassLayerConfig { budget = 0.2, greediness = 0.9, variance = 0.1 } } } }, southPole = new LandmassPainterConfig { temperature = 1.0, layerWarp = new VectorNoiseField.RelativeWarpConfig { frequency = 1.0, intensity = 0.64 }, layerPainter = new LayerPainter.Config { distanceWeighting = 0.1, cohesionWeighting = 2.0, layers = new StaticArray { new LandmassLayerConfig { budget = 0.0, greediness = 1.0, variance = 0.0 }, new LandmassLayerConfig { budget = 0.0, greediness = 1.0, variance = 0.0 }, new LandmassLayerConfig { budget = 0.04, greediness = 1.0, variance = 0.1 }, new LandmassLayerConfig { budget = 0.32, greediness = 1.0, variance = 0.2 } } } }, northPole = new LandmassPainterConfig { temperature = 1.0, layerWarp = new VectorNoiseField.RelativeWarpConfig { frequency = 1.0, intensity = 0.64 }, layerPainter = new LayerPainter.Config { distanceWeighting = 1.0, cohesionWeighting = 4.0, layers = new StaticArray { new LandmassLayerConfig { budget = 0.08, greediness = 0.2, variance = 0.3 }, new LandmassLayerConfig { budget = 0.15, greediness = 0.4, variance = 0.3 }, new LandmassLayerConfig { budget = 0.18, greediness = 0.7, variance = 0.3 }, new LandmassLayerConfig { budget = 0.1, greediness = 1.0, variance = 0.0 } } } } }, postprocessing = new RegionalConfig { main = new LandmassPostprocessingConfig { coastalTrimNoiseScale = 200.0, coastalTrimDistance = 80.0, coastalTrimVariance = 0.32, effectiveSpawnRadius = 280.0 }, southPole = new LandmassPostprocessingConfig { coastalTrimNoiseScale = 200.0, coastalTrimDistance = 80.0, coastalTrimVariance = 0.32, effectiveSpawnRadius = 280.0, smallIslandSizeThreshold = 180.0 }, northPole = new LandmassPostprocessingConfig { coastalTrimNoiseScale = 200.0, coastalTrimDistance = 80.0, coastalTrimVariance = 0.32, effectiveSpawnRadius = 280.0 } }, partitionMerging = new RegionalConfig { main = new LandmassPartitionMerger.Config { coastAffinity = 0.0, neighborAffinity = 1.0, mergeThreshold = 0.25, sizeAffinity = new Rmath.RemapConfig { fromA = 300.0, fromB = 800.0, toA = 0.5, toB = 0.0 }, borderLengthAffinity = new Rmath.RemapConfig { fromA = 700.0, fromB = 1400.0, toA = 0.0, toB = 1.0 } }, southPole = new LandmassPartitionMerger.Config { coastAffinity = 0.0, neighborAffinity = 1.0, mergeThreshold = 0.25, sizeAffinity = new Rmath.RemapConfig { fromA = 300.0, fromB = 800.0, toA = 0.5, toB = 0.0 }, borderLengthAffinity = new Rmath.RemapConfig { fromA = 700.0, fromB = 1400.0, toA = 0.0, toB = 1.0 } }, northPole = new LandmassPartitionMerger.Config { coastAffinity = 0.0, neighborAffinity = 1.0, mergeThreshold = 0.25, sizeAffinity = new Rmath.RemapConfig { fromA = 300.0, fromB = 800.0, toA = 0.5, toB = 0.0 }, borderLengthAffinity = new Rmath.RemapConfig { fromA = 700.0, fromB = 1400.0, toA = 0.0, toB = 1.0 } } } }; private static RiverConfig Rivers => new RiverConfig { precipitation = new RegionalConfig { main = { noiseScale = 1050.0, variability = 1.9, spawnMinRadius = 300.0, spawnMaxRadius = 1500.0, basinContribution = 0.7, minimum = 0.05 } }, origins = new RegionalConfig { main = new RiverOriginFinder.Config { density = 0.25, minCoastality = 0.8, minLandNeighbors = 2 } }, floodfill = new RegionalConfig { main = new RiverFillStage.Config { temperature = 0.35, maxOriginRandomLoss = 8.0, guide = new RiverFillGuide.Config { temperature = 1.0, loss = new RiverFillGuideLossCalculator.Config { energyPenaltyPow = 2.0, aestheticPenaltySteepness = 5.0, haltConfigurationLoss = 7.0, bendConfigurationLoss = 2.5, bendDeflectionLoss = 0.5, bendAestheticLoss = 0.35, forkDeflectionLoss = 1.0, forkImbalanceLoss = -0.2, forkAestheticLoss = 0.5, energyMult = 0.05, continuationLoss = 0.1 } } } }, width = new RegionalConfig { main = new RiverWidthConfig { scale = 140.0, power = 0.4, offset = -6.0 } }, prune = new RegionalConfig { main = new RiverPruneStage.Config { minStrahler = 2, minWidth = 14.5, maxWidthDiff = 2.2 } }, curiosities = new RegionalConfig { main = new RiverCuriosityConfig { sourceMult = 0.1, sourceMinLength = 150.0, sourceMaxLength = 800.0, proximityMult = 1.0, proximityScanRange = 240.0, proximityDifferentBasinsMultiplier = 1.5, proximityMinRatio = 2.5, proximityMaxRatio = 5.0, angleMult = 1.0, angleMinDegrees = 30.0, angleMaxDegrees = 100.0, angleNormWidth = 30.0, smoothingRange = 180.0 } }, channels = new RegionalConfig { main = new ChannelStage.Config { allowed = true, width = 40.0 }, southPole = new ChannelStage.Config { allowed = true, width = 40.0 }, northPole = new ChannelStage.Config { allowed = false, width = 40.0 } }, polylineIndex = new RegionalConfig { main = new RiverPolylineIndexBuilder.Config { deepSeaExtension = 120.0, oceanWidthMult = 1.5, sourceMaxWidth = 10.0, maxWidthChangePerMeter = 0.04, heightToSteepness = new Rmath.RemapConfig { fromA = 3.0, fromB = 20.0, toA = 2.3, toB = 3.5 } }, southPole = new RiverPolylineIndexBuilder.Config { deepSeaExtension = 120.0, oceanWidthMult = 1.5, sourceMaxWidth = 10.0, maxWidthChangePerMeter = 0.04, heightToSteepness = new Rmath.RemapConfig { fromA = 3.0, fromB = 20.0, toA = 2.3, toB = 3.5 } }, northPole = new RiverPolylineIndexBuilder.Config { deepSeaExtension = 120.0, oceanWidthMult = 1.5, sourceMaxWidth = 10.0, maxWidthChangePerMeter = 0.04, heightToSteepness = new Rmath.RemapConfig { fromA = 3.0, fromB = 20.0, toA = 2.3, toB = 3.5 } } }, meander = new RegionalConfig { main = new RiverMeanderer.Config { period = 3.4, riverAmplitude = 1.1, channelAmplitude = 0.75 }, southPole = new RiverMeanderer.Config { period = 3.4, riverAmplitude = 1.1, channelAmplitude = 0.0 }, northPole = new RiverMeanderer.Config { period = 3.4, riverAmplitude = 1.1, channelAmplitude = 0.75 } }, splines = new RegionalConfig { main = new RiverSplineBuilder.Config { resamplingRate = 1.4, widthVariationScale = 250.0, widthVariability = 0.25, meanderCurvatureMultiplier = 20.0, initialSplineType = RiverSplineBuilder.Config.SplineType.Hermite, resampledSplineType = RiverSplineBuilder.Config.SplineType.BSpline }, southPole = new RiverSplineBuilder.Config { resamplingRate = 1.4, widthVariationScale = 250.0, widthVariability = 0.6, meanderCurvatureMultiplier = 20.0, initialSplineType = RiverSplineBuilder.Config.SplineType.Hermite, resampledSplineType = RiverSplineBuilder.Config.SplineType.BSpline }, northPole = new RiverSplineBuilder.Config { resamplingRate = 1.4, widthVariationScale = 250.0, widthVariability = 0.25, meanderCurvatureMultiplier = 20.0, initialSplineType = RiverSplineBuilder.Config.SplineType.Hermite, resampledSplineType = RiverSplineBuilder.Config.SplineType.BSpline } } }; private static Riverheim.World.Splats.SplatConfig Splats => new Riverheim.World.Splats.SplatConfig { plateaus = new RegionalConfig { main = new PlateauConfig { spawnNoiseScale = 700.0, mountainPeaks = new PlateauMountainPeakConfig { spawnRate = 0.5, minHeightDiff = 4.0, size = new Rmath.Range { min = 55.0, max = 70.0 }, gradientClamping = new ClampingConfig { mode = ClampingConfig.Mode.Asymptotic, min = 0.3, max = 0.9, midpoint = 0.35, pow = 2.0 }, bulge = new Rpoint2 { x = 0.01, y = 0.0 }, cragThreshold = 0.65, cragHeight = new Rmath.Range { min = 32.0, max = 45.0 }, cragRadius = 4.0, cragTerrace = 0.0, allowedBiomes = new AllBiomeConfig { mountain = true } }, coastalCliffs = new PlateauSeasideCliffConfig { spawnRate = 0.45, neighborHeight = 0.3, minHeight = 19.0, maxHeight = 48.0, size = new Rmath.Range { min = 23.0, max = 28.0 }, gradientClamping = new ClampingConfig { mode = ClampingConfig.Mode.Asymptotic, min = 0.0, max = 0.65, midpoint = 0.6, pow = 2.0 }, bulge = new Rmath.Range { min = -0.002, max = 0.002 } }, hills = new PlateauHillConfig { spawnRate = 0.6, minHeightDiff = 5.0, size = new Rmath.Range { min = 25.0, max = 40.0 }, maxLength = 35.0, gradientClamping = new ClampingConfig { mode = ClampingConfig.Mode.Linear, min = 0.2, max = 0.35, rate = 1.0 }, bulge = new Rpoint2 { x = 0.0025, y = -0.0025 }, allowedBiomes = new AllBiomeConfig { meadows = true, forest = true, plains = true } } }, southPole = new PlateauConfig { spawnNoiseScale = 700.0, coastalCliffs = new PlateauSeasideCliffConfig { spawnRate = 0.5, minHeight = 19.0, maxHeight = 48.0, size = new Rmath.Range { min = 30.0, max = 50.0 }, gradientClamping = new ClampingConfig { mode = ClampingConfig.Mode.Asymptotic, min = 0.0, max = 0.65, midpoint = 0.6, pow = 2.0 }, bulge = new Rmath.Range { min = -0.002, max = 0.002 } } } }, riverSourceSplats = new RegionalConfig { main = new RiverSourceSplatStage.Config { allowedBiomes = new AllBiomeConfig { meadows = true, forest = true, swamp = true, plains = true, mistlands = true, mistglade = true }, crags = new RiverSourceSplatStage.CragConfig { frequency = 0.65, height = new RiverSourceSplatStage.SpawnFeatureAffinityConfig { type = RiverSourceSplatStage.SpawnFeatureAffinityConfig.AffinityType.Both, lowerBound = Rmath.Range.From(0.0, 6.0), upperBound = Rmath.Range.From(12.0, 25.0) }, random = new CragRandomizer.Config { height = new Rmath.Range { min = 14.0, max = 32.0 }, radius = new Rmath.Range { min = 2.0, max = 3.0 }, terrace = new Rmath.Range { min = 0.14, max = 0.16 } } }, ponds = new RiverSourceSplatStage.PondConfig { frequency = 0.6, height = new RiverSourceSplatStage.SpawnFeatureAffinityConfig { type = RiverSourceSplatStage.SpawnFeatureAffinityConfig.AffinityType.Both, lowerBound = Rmath.Range.From(0.5, 4.0), upperBound = Rmath.Range.From(6.5, 10.0) }, riverLength = new RiverSourceSplatStage.SpawnFeatureAffinityConfig { type = RiverSourceSplatStage.SpawnFeatureAffinityConfig.AffinityType.LowerBound, lowerBound = Rmath.Range.From(200.0, 350.0) }, stepback = 20.0, random = new PondRandomizer.Config { height = new Rmath.Range { min = -0.7, max = -0.5 }, radius = new Rmath.Range { min = 9.0, max = 14.0 } } } } } }; public static RenderingConfig RenderingConfig() { RenderingConfig result = default(RenderingConfig); result.heightmap = new HeightMapBuilder.Config { resolution = 8.0, method = HeightMapBuilder.Config.Method.Barycentric, distanceWeighted = new DistanceWeightedInterpolator.Config { truncation = DistanceWeightedInterpolator.Config.Truncation.SmoothStep }, inverseDistance = new RadialKernels.InverseDistanceConfig { power = 1.7, reach = 1.0, epsilon = 0.0 }, gaussian = new RadialKernels.GaussianConfig { sigma = 0.48, reach = 4.0 }, warpIntensity = 0.4 }; result.renderer = new WorldRenderer.Config { interpolationMethod = BicubicInterpolator.Method.BSpline, biomeWarp = new VectorNoiseField.RelativeWarpConfig { frequency = 2.5, intensity = 0.25 }, rivers = new RiverRenderer.Config { isEnabled = true, spatialIndexSpacing = 30.0, sdfMergingRange = 15.0, width = new RiverRenderer.WidthConfig { depthPower = 0.8, minWidth = 2.5 }, tesselation = new TesselationConfig { minStep = 0.5, maxStep = 30.0, stepRatio = 0.75 }, profile = new RiverRenderer.ProfileConfig { minDepth = 2.5, depthSmoothness = 0.8, widthToDepth = 3.3, maxBankHeight = 80.0, lerpingMargin = 30.0, bankSmoothness = 3.0, noiseAmplitude = 10.0, noiseKickInStart = 0.2, noiseKickInEnd = 1.2 } }, biomes = new BiomeRenderer.Config { isEnabled = true, coarse = new CommonBiomeRenderer.QuadNoise.Config { coarse1 = 100.0, coarse2 = 50.0, fine1 = 20.0, fine2 = 10.0 }, ocean = new CommonBiomeRenderer.Config { clutter = new ClutterRenderer.Params { amplitude = 2.0, ubiquity = 0.5, gain = 0.3, clamping = 0.8, offset = 0.7 }, fertility = 1.0 }, meadows = new MeadowsRenderer.Config { coarseMagnitude = 10.0, cliffDensityThreshold = 0.475, cliffDensityMargin = 0.023, cliffMaxHeight = 4.0, cliffNoiseScale = 40.0, cliffNoiseMargin = 0.07, cliffHeightStartRange = new Rmath.Range { min = 1.5, max = 8.0 }, clutter = new ClutterRenderer.Params { amplitude = 3.0, ubiquity = 0.5, gain = 0.6, clamping = 0.4, offset = -0.4, bias = 0.24 } }, marsh = new CommonBiomeRenderer.Config { clutter = new ClutterRenderer.Params { amplitude = 3.0, ubiquity = 0.58, gain = 0.4, clamping = 0.0, offset = 0.0, bias = 0.15 }, fertility = 1.0 }, forest = new CommonBiomeRenderer.Config { coarseAmplitude = 25.0, clutter = new ClutterRenderer.Params { amplitude = 2.8, ubiquity = 0.65, gain = 0.75, clamping = 0.125, offset = -0.125, bias = 0.38 }, fertility = 1.0 }, swamp = new SwampRenderer.Config { coarseMagnitude = 7.5, coarseScale1 = 25.0, coarseScale2 = 12.5, pathVerticalCutoff = 0.95, pathBandWidth = 30.0, pathBandDepth = -14.75, pathNoiseScale = 200.0, pathNoiseIntensity = 0.6, pathWarp = new VectorNoiseField.RelativeWarpConfig { frequency = 2.0, intensity = 0.15 }, pathBlendingStrength = 0.5, clutter = new ClutterRenderer.Params { amplitude = 1.6, ubiquity = 0.7, gain = 0.85, clamping = 0.4, offset = 0.3, bias = 0.22 } }, mountain = new CommonBiomeRenderer.Config { coarseAmplitude = 20.0, clutter = new ClutterRenderer.Params { amplitude = 3.2, ubiquity = 0.55, gain = 0.9, clamping = 0.0, offset = 0.0, bias = 0.31 }, fertility = 1.0 }, plains = new CommonBiomeRenderer.Config { coarseAmplitude = 10.0, clutter = new ClutterRenderer.Params { amplitude = 3.5, ubiquity = 0.45, gain = 0.65, clamping = 0.8, offset = -0.5, bias = 0.21 }, fertility = 1.0 }, mistlands = new MistlandsRenderer.Config { coarseMagnitude = 130.0, coarse = new CommonBiomeRenderer.QuadNoise.Config { coarse1 = 75.0, coarse2 = 37.0, fine1 = 48.0, fine2 = 30.0, offset = -0.035 }, pow = 1.5, bias = -8.0, hillClutter = new ClutterRenderer.Params { amplitude = 5.0, ubiquity = 1.0, gain = 0.9, clamping = 0.0, offset = 0.0 }, valleyClutter = new ClutterRenderer.Params { amplitude = 1.8, ubiquity = 0.5, gain = 0.5, clamping = 0.5, offset = 0.5, bias = 0.45 }, slopeToFertilityRemap = new Rmath.Range { min = 0.75, max = 0.35 }, terracingMult = 0.25 }, mistglade = new MistgladeRenderer.Config { courseScale = 50.0, coarseMagnitude = 3.0, slabScale = 40.0, slabHeight = 2.0, slabFractal = new NoiseField.FractalConfig { type = NoiseField.FractalConfig.FractalType.Min, octaves = 3, lacunarity = 1.15, gain = 0.9, smoothing = 0.1 }, slabHeightRemap = new Rmath.Range { min = 0.33, max = 0.38 }, slabCrackScale = 25.0, slabCrackDepth = 1.0, slopeToFertilityRemap = new Rmath.Range { min = 0.75, max = 0.35 }, clutter = new ClutterRenderer.Params { amplitude = 1.8, ubiquity = 0.55, gain = 0.5, clamping = 0.5, offset = -0.5, bias = 0.33 }, slabClutter = new ClutterRenderer.Params { amplitude = 2.0, ubiquity = 1.0, gain = 0.8, clamping = 0.2, offset = 0.0, bias = 0.5 } }, ashlands = new CommonBiomeRenderer.Config { clutter = new ClutterRenderer.Params { amplitude = 3.0, ubiquity = 0.5, gain = 0.7, clamping = 0.95, offset = -0.5, bias = 0.34 } }, lavaplain = new LavaplainRenderer.Config { lavaCellScale = 12.5, lavaCellFractal = new NoiseField.FractalConfig { octaves = 3, lacunarity = 2.0, gain = 0.5 }, lavaCellMultiplier = 2.0, lavaCellPower = 4, lavaGradScale = 100.0, lavaGradFractal = new NoiseField.FractalConfig { octaves = 3, lacunarity = 2.0, gain = 0.5 }, lavaGradRemapMin = 0.75, lavaGradRemapMax = 1.0, lavaGradPower = 2, lavaGradHeightRemap = new Rmath.RemapConfig { fromA = 3.0, fromB = 12.0, toA = 0.5, toB = 1.0 }, lavaDepressionNoiseScale = 20.0, lavaDepressionNoisePower = 2.0, lavaMinDepression = 2.0, lavaMaxDepression = 11.0, lavaMinHeightClamp = 2.0, clutter = new ClutterRenderer.Params { amplitude = 3.0, ubiquity = 0.5, gain = 0.7, clamping = 0.95, offset = -0.5, bias = 0.34 } }, deepNorth = new CommonBiomeRenderer.Config { coarseAmplitude = 30.0, clutter = new ClutterRenderer.Params { amplitude = 2.5, ubiquity = 0.5, gain = 0.6, bias = 0.14 }, fertility = 1.0 } }, shaping = new WorldRenderer.ShapingConfig { terracingStep = 2.0, worldEdgeWidth = 20.0, offworldDepth = -430.0 }, cliffs = new CliffRenderer.Config { isEnabled = true, ramping = new ProfileUtility.SmoothRamp.Config { slope = 1.0, reach = 0.8, power = 4.0 }, gradientMagnitude = 6.0, terracing = new CliffRenderer.TerracingConfig { height = 30.0, variability = 1.05, varianceFrequency = 1.5, slopeFalloff = 0.6 } }, splats = new Riverheim.Rendering.SplatConfig { isEnabled = true, profile = new ProfileUtility.Rational.Config { slope = -8.0, scale = 200.0 }, smaxBlendRange = 4.0, sminBlendRange = 2.0, warp = new VectorNoiseField.AbsoluteWarpConfig { period = 70.0, intensity = 0.25 }, noiseAmplitude = 8.0, rangeOvershoot = 10.0, crags = new CragRenderConfig { profile = new ProfileUtility.Rational.Config { slope = -2.5, scale = 2.0 }, profileVariability = 4.0, terraceVariability = 0.3 }, ponds = new PondRenderConfig { blendRange = 20.0, contourVarianceRange = 25.0 } }, profileNoise = new WorldRenderer.ProfileNoiseConfig { featureSize = 20.0, fractal = new NoiseField.FractalConfig { type = NoiseField.FractalConfig.FractalType.Max, octaves = 3, lacunarity = 2.3, gain = 0.75, smoothing = 0.1 }, bias = -0.5 }, clutter = new ClutterRenderer.Config { isEnabled = true, envelopeScale = 90.0, envelopeFractal = new NoiseField.FractalConfig { gain = 0.6, lacunarity = 2.5, octaves = 3 }, featureSize = 12.0, octaves = 3, lacunarity = 1.8, envelopeTransitionWindow = 0.2, envelopeOctaveOffset = -0.04, smoothingRange = 0.15, ultrafineScale = 2.4, ultrafineAmplitude = 0.1 } }; return result; } public static CommonConfig CommonConfig() { CommonConfig result = default(CommonConfig); result.worldSize = 10500.0; result.tileSpacing = 60.0; result.mountainHeight = 50.0; result.oceanDepth = -40.0; result.startingAreaRadius = 870.0; return result; } public static WorldConfig Config() { return new WorldConfig { tiling = new WorldVerticesCalculator.Config { lodLevels = 5, lodScale = 2.0 }, polar = new PolarBeltConfig { offset = 4000.0, radius = 12000.0, noiseScale = 500.0, noiseAmplitude = 400.0 }, travel = new RegionalConfig { main = new TravelDistanceStage.Config { normalize = true, landCost = 1.0, riverCost = 1.5, oceanCost = 0.85, embarkCost = 5.0 }, northPole = new TravelDistanceStage.Config { landCost = 1.0 }, southPole = new TravelDistanceStage.Config { landCost = 1.0, riverCost = 1.0, oceanCost = 1.0, embarkCost = 3.0, frontier = new TravelDistanceStage.FrontierRaymarchingConfig { rays = 256, maxSteps = 15, stepRate = 0.9, noiseScale = 250.0, noiseMagnitude = 0.025 } } }, landmass = Landmass, rivers = Rivers, height = Height, biomes = Biomes, splats = Splats, forestation = new RegionalConfig { main = new ForestationStage.Config { noiseScale = 250.0, noiseFractal = new NoiseField.FractalConfig { octaves = 4, lacunarity = 1.6, gain = 0.7 }, marshBonus = 0.2, plainsRiverBonus = 0.25, plainsRiverBonusProximityRange = new Rmath.Range { min = 0.0, max = 100.0 }, plainsRiverBonusHeightRange = new Rmath.Range { min = 0.0, max = 12.0 }, meadowsRiverBonus = -0.14, meadowsRiverBonusProximityRange = new Rmath.Range { min = 0.0, max = 50.0 } }, northPole = new ForestationStage.Config { noiseScale = 250.0, noiseFractal = new NoiseField.FractalConfig { octaves = 4, lacunarity = 1.6, gain = 0.7 } }, southPole = new ForestationStage.Config { noiseScale = 250.0, noiseFractal = new NoiseField.FractalConfig { octaves = 4, lacunarity = 1.6, gain = 0.7 } } } }; } } public static class WorldSettingSerialization { public class Error : Exception { public Error(string msg) : base(msg) { } } public struct Setting { public string name; public object value; } private const string SETTING_PRESET = "preset"; private const string SETTING_PRESET_VERSION = "preset.version"; public static WorldSettings.SettingType GetSettingType(string settingName) { if (settingName == "preset") { return WorldSettings.SettingType.String; } if (settingName == "preset.version") { return WorldSettings.SettingType.Int; } throw new Error("setting " + settingName + " is unknown to the runtime"); } public static WorldSettings Deserialize(Setting[] settings) { Dictionary dictionary = new Dictionary(); for (int i = 0; i < settings.Length; i++) { Setting setting = settings[i]; dictionary[setting.name] = setting.value; } if (!dictionary.TryGetValue("preset", out var value)) { throw new Error("Missing preset"); } string preset = (string)value; if (!dictionary.TryGetValue("preset.version", out var value2)) { throw new Error("Missing preset version"); } WorldSettings worldSettings = new WorldSettings(preset, (int)value2); if (!ConfigManager.Validate(worldSettings, out var message)) { throw new Error(message); } return worldSettings; } public static Setting[] Serialize(WorldSettings settings) { List list = new List { new Setting { name = "preset", value = settings.Preset }, new Setting { name = "preset.version", value = settings.PresetVersion } }; WorldSettings.Feature[] features = settings.Features; int num = 0; if (num < features.Length) { _ = features[num]; throw new NotImplementedException("custom features are not supported yet"); } WorldSettings.Parameter[] parameters = settings.Parameters; num = 0; if (num < parameters.Length) { _ = parameters[num]; throw new NotImplementedException("custom parameters are not supported yet"); } return list.ToArray(); } } public record WorldSettings { public enum SettingType { String, Int, Bool, Float, Double } public record Feature { public string Name { get; } public int Version { get; } public Feature(string name, int version) { Name = name; Version = version; } } public record Parameter { public string Name { get; } public SettingType Type { get; } public object Value { get; } public Parameter(string name, SettingType type, object value) { Name = name; Type = type; Value = value; } } public string Preset { get; } public int PresetVersion { get; } public Feature[] Features { get; } public Parameter[] Parameters { get; } public WorldSettings(string preset, int presetVersion) : this(preset, presetVersion, Array.Empty(), Array.Empty()) { } public WorldSettings(string preset, int presetVersion, Feature[] features, Parameter[] parameters) { Preset = preset; PresetVersion = presetVersion; Features = features; Parameters = parameters; } } } namespace Riverheim.Configuration.Extensions { public class ExperimentalPresetHandler : IPresetHandler { private readonly int rootPresetVersion; public ExperimentalPresetHandler(int rootPresetVersion) { this.rootPresetVersion = rootPresetVersion; } public string ParentPreset() { return "builtin.amur"; } public int ParentPresetVersion(int version) { return rootPresetVersion; } public int MaxSupportedVersion() { return 0; } public WorldGenerationConfig GetConfig(int version, WorldGenerationConfig parentConfig) { WorldGenerationConfig result = parentConfig; result.world.height.flatlands.main.noiseFractal.type = NoiseField.FractalConfig.FractalType.Min; result.world.height.flatlands.main.noiseFractal.octaves = 3; result.world.height.flatlands.main.noiseFractal.lacunarity = 1.2; result.world.height.flatlands.main.noiseFractal.gain = 0.8; result.world.height.flatlands.main.noiseThreshold = 0.275; result.world.height.boundary.main.coastalBonus = -0.7; result.world.height.incline.main.land.rivers.method = DifferentialInclineCalculator.RiverConfig.Method.NormDiff; result.world.height.incline.main.land.mult = 1.06; result.world.height.incline.main.land.flats.multiplier = 0.17; result.world.height.incline.main.land.penalties.flatlandPenalty = 0.5; result.world.height.incline.main.land.penalties.riverConnection = -0.75; result.world.height.incline.main.land.penalties.riverAdjacency = -0.25; result.world.height.shaping.main.land.smoothRamp.reach = 15.0; result.world.height.shaping.main.land.bias = 2.0; result.world.splats.plateaus.main.mountainPeaks.spawnRate = 0.0; return result; } } public interface IPresetHandler { string ParentPreset(); int ParentPresetVersion(int version); int MaxSupportedVersion(); WorldGenerationConfig GetConfig(int version, WorldGenerationConfig parentConfig); } } namespace DelaunatorSharp { internal class Delaunator { private readonly double EPSILON = Math.Pow(2.0, -52.0); private readonly int[] EDGE_STACK = new int[512]; private readonly int hashSize; private readonly int[] hullPrev; private readonly int[] hullNext; private readonly int[] hullTri; private readonly int[] hullHash; private double cx; private double cy; private int trianglesLen; private readonly double[] coords; private readonly int hullStart; private readonly int hullSize; public int[] Triangles { get; private set; } public int[] Halfedges { get; private set; } public IPoint[] Points { get; private set; } public int[] Hull { get; private set; } public Delaunator(IPoint[] points) { if (points.Length < 3) { throw new ArgumentOutOfRangeException("Need at least 3 points"); } Points = points; coords = new double[Points.Length * 2]; for (int i = 0; i < Points.Length; i++) { IPoint IPoint2 = Points[i]; coords[2 * i] = IPoint2.X; coords[2 * i + 1] = IPoint2.Y; } int num = points.Length; int num2 = 2 * num - 5; Triangles = new int[num2 * 3]; Halfedges = new int[num2 * 3]; hashSize = (int)Math.Ceiling(Math.Sqrt(num)); hullPrev = new int[num]; hullNext = new int[num]; hullTri = new int[num]; hullHash = new int[hashSize]; int[] array = new int[num]; double num3 = double.PositiveInfinity; double num4 = double.PositiveInfinity; double num5 = double.NegativeInfinity; double num6 = double.NegativeInfinity; for (int j = 0; j < num; j++) { double num7 = coords[2 * j]; double num8 = coords[2 * j + 1]; if (num7 < num3) { num3 = num7; } if (num8 < num4) { num4 = num8; } if (num7 > num5) { num5 = num7; } if (num8 > num6) { num6 = num8; } array[j] = j; } double ax = (num3 + num5) / 2.0; double ay = (num4 + num6) / 2.0; double num9 = double.PositiveInfinity; int num10 = 0; int num11 = 0; int num12 = 0; for (int k = 0; k < num; k++) { double num13 = Dist(ax, ay, coords[2 * k], coords[2 * k + 1]); if (num13 < num9) { num10 = k; num9 = num13; } } double num14 = coords[2 * num10]; double num15 = coords[2 * num10 + 1]; num9 = double.PositiveInfinity; for (int l = 0; l < num; l++) { if (l != num10) { double num16 = Dist(num14, num15, coords[2 * l], coords[2 * l + 1]); if (num16 < num9 && num16 > 0.0) { num11 = l; num9 = num16; } } } double num17 = coords[2 * num11]; double num18 = coords[2 * num11 + 1]; double num19 = double.PositiveInfinity; for (int m = 0; m < num; m++) { if (m != num10 && m != num11) { double num20 = Circumradius(num14, num15, num17, num18, coords[2 * m], coords[2 * m + 1]); if (num20 < num19) { num12 = m; num19 = num20; } } } double num21 = coords[2 * num12]; double num22 = coords[2 * num12 + 1]; if (num19 == double.PositiveInfinity) { throw new Exception("No Delaunay triangulation exists for this input."); } if (Orient(num14, num15, num17, num18, num21, num22)) { int num23 = num11; double num24 = num17; double num25 = num18; num11 = num12; num17 = num21; num18 = num22; num12 = num23; num21 = num24; num22 = num25; } Point Point2 = Circumcenter(num14, num15, num17, num18, num21, num22); cx = Point2.X; cy = Point2.Y; double[] array2 = new double[num]; for (int n = 0; n < num; n++) { array2[n] = Dist(coords[2 * n], coords[2 * n + 1], Point2.X, Point2.Y); } Quicksort(array, array2, 0, num - 1); hullStart = num10; hullSize = 3; hullNext[num10] = (hullPrev[num12] = num11); hullNext[num11] = (hullPrev[num10] = num12); hullNext[num12] = (hullPrev[num11] = num10); hullTri[num10] = 0; hullTri[num11] = 1; hullTri[num12] = 2; hullHash[HashKey(num14, num15)] = num10; hullHash[HashKey(num17, num18)] = num11; hullHash[HashKey(num21, num22)] = num12; trianglesLen = 0; AddTriangle(num10, num11, num12, -1, -1, -1); double num26 = 0.0; double num27 = 0.0; for (int num28 = 0; num28 < array.Length; num28++) { int num29 = array[num28]; double num30 = coords[2 * num29]; double num31 = coords[2 * num29 + 1]; if (num28 > 0 && Math.Abs(num30 - num26) <= EPSILON && Math.Abs(num31 - num27) <= EPSILON) { continue; } num26 = num30; num27 = num31; if (num29 == num10 || num29 == num11 || num29 == num12) { continue; } int num32 = 0; for (int num33 = 0; num33 < hashSize; num33++) { int num34 = HashKey(num30, num31); num32 = hullHash[(num34 + num33) % hashSize]; if (num32 != -1 && num32 != hullNext[num32]) { break; } } num32 = hullPrev[num32]; int num35 = num32; int num36 = hullNext[num35]; while (!Orient(num30, num31, coords[2 * num35], coords[2 * num35 + 1], coords[2 * num36], coords[2 * num36 + 1])) { num35 = num36; if (num35 == num32) { num35 = int.MaxValue; break; } num36 = hullNext[num35]; } if (num35 == int.MaxValue) { continue; } int num37 = AddTriangle(num35, num29, hullNext[num35], -1, -1, hullTri[num35]); hullTri[num29] = Legalize(num37 + 2); hullTri[num35] = num37; hullSize++; int num38 = hullNext[num35]; num36 = hullNext[num38]; while (Orient(num30, num31, coords[2 * num38], coords[2 * num38 + 1], coords[2 * num36], coords[2 * num36 + 1])) { num37 = AddTriangle(num38, num29, num36, hullTri[num29], -1, hullTri[num38]); hullTri[num29] = Legalize(num37 + 2); hullNext[num38] = num38; hullSize--; num38 = num36; num36 = hullNext[num38]; } if (num35 == num32) { num36 = hullPrev[num35]; while (Orient(num30, num31, coords[2 * num36], coords[2 * num36 + 1], coords[2 * num35], coords[2 * num35 + 1])) { num37 = AddTriangle(num36, num29, num35, -1, hullTri[num35], hullTri[num36]); Legalize(num37 + 2); hullTri[num36] = num37; hullNext[num35] = num35; hullSize--; num35 = num36; num36 = hullPrev[num35]; } } hullStart = (hullPrev[num29] = num35); hullNext[num35] = (hullPrev[num38] = num29); hullNext[num29] = num38; hullHash[HashKey(num30, num31)] = num29; hullHash[HashKey(coords[2 * num35], coords[2 * num35 + 1])] = num35; } Hull = new int[hullSize]; int num39 = hullStart; for (int num40 = 0; num40 < hullSize; num40++) { Hull[num40] = num39; num39 = hullNext[num39]; } hullPrev = (hullNext = (hullTri = null)); Triangles = Triangles.Take(trianglesLen).ToArray(); Halfedges = Halfedges.Take(trianglesLen).ToArray(); } private int Legalize(int a) { int num = 0; int num4; while (true) { int num2 = Halfedges[a]; int num3 = a - a % 3; num4 = num3 + (a + 2) % 3; if (num2 == -1) { if (num == 0) { break; } a = EDGE_STACK[--num]; continue; } int num5 = num2 - num2 % 3; int num6 = num3 + (a + 1) % 3; int num7 = num5 + (num2 + 2) % 3; int num8 = Triangles[num4]; int num9 = Triangles[a]; int num10 = Triangles[num6]; int num11 = Triangles[num7]; if (InCircle(coords[2 * num8], coords[2 * num8 + 1], coords[2 * num9], coords[2 * num9 + 1], coords[2 * num10], coords[2 * num10 + 1], coords[2 * num11], coords[2 * num11 + 1])) { Triangles[a] = num11; Triangles[num2] = num8; int num12 = Halfedges[num7]; if (num12 == -1) { int num13 = hullStart; do { if (hullTri[num13] == num7) { hullTri[num13] = a; break; } num13 = hullPrev[num13]; } while (num13 != hullStart); } Link(a, num12); Link(num2, Halfedges[num4]); Link(num4, num7); int num14 = num5 + (num2 + 1) % 3; if (num < EDGE_STACK.Length) { EDGE_STACK[num++] = num14; } } else { if (num == 0) { break; } a = EDGE_STACK[--num]; } } return num4; } private static bool InCircle(double ax, double ay, double bx, double by, double cx, double cy, double px, double py) { double num = ax - px; double num2 = ay - py; double num3 = bx - px; double num4 = by - py; double num5 = cx - px; double num6 = cy - py; double num7 = num * num + num2 * num2; double num8 = num3 * num3 + num4 * num4; double num9 = num5 * num5 + num6 * num6; return num * (num4 * num9 - num8 * num6) - num2 * (num3 * num9 - num8 * num5) + num7 * (num3 * num6 - num4 * num5) < 0.0; } private int AddTriangle(int i0, int i1, int i2, int a, int b, int c) { int num = trianglesLen; Triangles[num] = i0; Triangles[num + 1] = i1; Triangles[num + 2] = i2; Link(num, a); Link(num + 1, b); Link(num + 2, c); trianglesLen += 3; return num; } private void Link(int a, int b) { Halfedges[a] = b; if (b != -1) { Halfedges[b] = a; } } private int HashKey(double x, double y) { return (int)(Math.Floor(PseudoAngle(x - cx, y - cy) * (double)hashSize) % (double)hashSize); } private static double PseudoAngle(double dx, double dy) { double num = dx / (Math.Abs(dx) + Math.Abs(dy)); return ((dy > 0.0) ? (3.0 - num) : (1.0 + num)) / 4.0; } private static void Quicksort(int[] ids, double[] dists, int left, int right) { if (right - left <= 20) { for (int i = left + 1; i <= right; i++) { int num = ids[i]; double num2 = dists[num]; int num3 = i - 1; while (num3 >= left && dists[ids[num3]] > num2) { ids[num3 + 1] = ids[num3--]; } ids[num3 + 1] = num; } return; } int i2 = left + right >> 1; int num4 = left + 1; int num5 = right; Swap(ids, i2, num4); if (dists[ids[left]] > dists[ids[right]]) { Swap(ids, left, right); } if (dists[ids[num4]] > dists[ids[right]]) { Swap(ids, num4, right); } if (dists[ids[left]] > dists[ids[num4]]) { Swap(ids, left, num4); } int num6 = ids[num4]; double num7 = dists[num6]; while (true) { num4++; if (!(dists[ids[num4]] < num7)) { do { num5--; } while (dists[ids[num5]] > num7); if (num5 < num4) { break; } Swap(ids, num4, num5); } } ids[left + 1] = ids[num5]; ids[num5] = num6; if (right - num4 + 1 >= num5 - left) { Quicksort(ids, dists, num4, right); Quicksort(ids, dists, left, num5 - 1); } else { Quicksort(ids, dists, left, num5 - 1); Quicksort(ids, dists, num4, right); } } private static void Swap(int[] arr, int i, int j) { int num = arr[i]; arr[i] = arr[j]; arr[j] = num; } private static bool Orient(double px, double py, double qx, double qy, double rx, double ry) { return (qy - py) * (rx - qx) - (qx - px) * (ry - qy) < 0.0; } private static double Circumradius(double ax, double ay, double bx, double by, double cx, double cy) { double num = bx - ax; double num2 = by - ay; double num3 = cx - ax; double num4 = cy - ay; double num5 = num * num + num2 * num2; double num6 = num3 * num3 + num4 * num4; double num7 = 0.5 / (num * num4 - num2 * num3); double num8 = (num4 * num5 - num2 * num6) * num7; double num9 = (num * num6 - num3 * num5) * num7; return num8 * num8 + num9 * num9; } private static Point Circumcenter(double ax, double ay, double bx, double by, double cx, double cy) { double num = bx - ax; double num2 = by - ay; double num3 = cx - ax; double num4 = cy - ay; double num5 = num * num + num2 * num2; double num6 = num3 * num3 + num4 * num4; double num7 = 0.5 / (num * num4 - num2 * num3); double x = ax + (num4 * num5 - num2 * num6) * num7; double y = ay + (num * num6 - num3 * num5) * num7; return new Point(x, y); } private static double Dist(double ax, double ay, double bx, double by) { double num = ax - bx; double num2 = ay - by; return num * num + num2 * num2; } public IEnumerable<ITriangle> GetTriangles() { for (int t = 0; t < Triangles.Length / 3; t++) { yield return new Triangle(t, GetTrianglePoints(t)); } } public IEnumerable<IEdge> GetEdges() { for (int e = 0; e < Triangles.Length; e++) { if (e > Halfedges[e]) { IPoint p = Points[Triangles[e]]; IPoint q = Points[Triangles[NextHalfedge(e)]]; yield return new Edge(e, p, q); } } } public IEnumerable<IEdge> GetVoronoiEdges(FuncIPoint> triangleVerticeSelector = null) { if (triangleVerticeSelector == null) { triangleVerticeSelector = (int x) => GetCentroid(x); } for (int e = 0; e < Triangles.Length; e++) { if (e < Halfedges[e]) { IPoint p = triangleVerticeSelector(TriangleOfEdge(e)); IPoint q = triangleVerticeSelector(TriangleOfEdge(Halfedges[e])); yield return new Edge(e, p, q); } } } public IEnumerable<IEdge> GetVoronoiEdgesBasedOnCircumCenter() { return GetVoronoiEdges(GetTriangleCircumcenter); } public IEnumerable<IEdge> GetVoronoiEdgesBasedOnCentroids() { return GetVoronoiEdges(GetCentroid); } public IEnumerable<IVoronoiCell> GetVoronoiCells(FuncIPoint> triangleVerticeSelector = null) { if (triangleVerticeSelector == null) { triangleVerticeSelector = (int x) => GetCentroid(x); } HashSet seen = new HashSet(); List<IPoint> vertices = new List<IPoint>(10); for (int e = 0; e < Triangles.Length; e++) { int num = Triangles[NextHalfedge(e)]; if (!seen.Add(num)) { continue; } foreach (int item in EdgesAroundPoint(e)) { vertices.Add(triangleVerticeSelector(TriangleOfEdge(item))); } yield return new VoronoiCell(num, vertices.ToArray()); vertices.Clear(); } } public IEnumerable<IVoronoiCell> GetVoronoiCellsBasedOnCircumcenters() { return GetVoronoiCells(GetTriangleCircumcenter); } public IEnumerable<IVoronoiCell> GetVoronoiCellsBasedOnCentroids() { return GetVoronoiCells(GetCentroid); } public IEnumerable<IEdge> GetHullEdges() { return CreateHull(GetHullPoints()); } public IPoint[] GetHullPoints() { return Array.ConvertAll(Hull, (int x) => Points[x]); } public IPoint[] GetTrianglePoints(int t) { List<IPoint> list = new List<IPoint>(); foreach (int item in PointsOfTriangle(t)) { list.Add(Points[item]); } return list.ToArray(); } public IPoint[] GetRellaxedPoints() { List<IPoint> list = new List<IPoint>(); foreach (IVoronoiCell voronoiCellsBasedOnCircumcenter in GetVoronoiCellsBasedOnCircumcenters()) { list.Add(GetCentroid(voronoiCellsBasedOnCircumcenter.Points)); } return list.ToArray(); } public IEnumerable<IEdge> GetEdgesOfTriangle(int t) { return CreateHull(from p in EdgesOfTriangle(t) select Points[p]); } public static IEnumerable<IEdge> CreateHull(IEnumerable<IPoint> points) { return points.Zip(points.Skip(1).Append<IPoint>(points.FirstOrDefault()), (IPoint a, IPoint b) => new Edge(0, a, b)).OfType<IEdge>(); } public IPoint GetTriangleCircumcenter(int t) { IPoint[] trianglePoints = GetTrianglePoints(t); return GetCircumcenter(trianglePoints[0], trianglePoints[1], trianglePoints[2]); } public IPoint GetCentroid(int t) { return GetCentroid(GetTrianglePoints(t)); } public static IPoint GetCircumcenter(IPoint a, IPoint b, IPoint c) { return Circumcenter(a.X, a.Y, b.X, b.Y, c.X, c.Y); } public static IPoint GetCentroid(IPoint[] points) { double num = 0.0; double num2 = 0.0; double num3 = 0.0; int num4 = 0; int num5 = points.Length - 1; while (num4 < points.Length) { double num6 = points[num4].X * points[num5].Y - points[num5].X * points[num4].Y; num += num6; num2 += (points[num4].X + points[num5].X) * num6; num3 += (points[num4].Y + points[num5].Y) * num6; num5 = num4++; } if (Math.Abs(num) < 1.0000000116860974E-07) { return default(Point); } num *= 3.0; return new Point(num2 / num, num3 / num); } public void ForEachTriangle(Action<ITriangle> callback) { foreach (ITriangle triangle in GetTriangles()) { callback?.Invoke(triangle); } } public void ForEachTriangleEdge(Action<IEdge> callback) { foreach (IEdge edge in GetEdges()) { callback?.Invoke(edge); } } public void ForEachVoronoiEdge(Action<IEdge> callback) { foreach (IEdge voronoiEdge in GetVoronoiEdges()) { callback?.Invoke(voronoiEdge); } } public void ForEachVoronoiCellBasedOnCentroids(Action<IVoronoiCell> callback) { foreach (IVoronoiCell voronoiCellsBasedOnCentroid in GetVoronoiCellsBasedOnCentroids()) { callback?.Invoke(voronoiCellsBasedOnCentroid); } } public void ForEachVoronoiCellBasedOnCircumcenters(Action<IVoronoiCell> callback) { foreach (IVoronoiCell voronoiCellsBasedOnCircumcenter in GetVoronoiCellsBasedOnCircumcenters()) { callback?.Invoke(voronoiCellsBasedOnCircumcenter); } } public void ForEachVoronoiCell(Action<IVoronoiCell> callback, FuncIPoint> triangleVertexSelector = null) { foreach (IVoronoiCell voronoiCell in GetVoronoiCells(triangleVertexSelector)) { callback?.Invoke(voronoiCell); } } public IEnumerable EdgesAroundPoint(int start) { int incoming = start; do { yield return incoming; int num = NextHalfedge(incoming); incoming = Halfedges[num]; } while (incoming != -1 && incoming != start); } public IEnumerable PointsOfTriangle(int t) { int[] array = EdgesOfTriangle(t); foreach (int num in array) { yield return Triangles[num]; } } public IEnumerable TrianglesAdjacentToTriangle(int t) { List list = new List(); int[] array = EdgesOfTriangle(t); foreach (int num in array) { int num2 = Halfedges[num]; if (num2 >= 0) { list.Add(TriangleOfEdge(num2)); } } return list; } public static int NextHalfedge(int e) { if (e % 3 != 2) { return e + 1; } return e - 2; } public static int PreviousHalfedge(int e) { if (e % 3 != 0) { return e - 1; } return e + 2; } public static int[] EdgesOfTriangle(int t) { return new int[3] { 3 * t, 3 * t + 1, 3 * t + 2 }; } public static int TriangleOfEdge(int e) { return e / 3; } } internal interface IEdge { IPoint P { get; } IPoint Q { get; } int Index { get; } } internal interface IPoint { double X { get; set; } double Y { get; set; } } internal interface ITriangle { IEnumerable<IPoint> Points { get; } int Index { get; } } internal interface IVoronoiCell { IPoint[] Points { get; } int Index { get; } } internal struct Edge : IEdge { public IPoint P { get; set; } public IPoint Q { get; set; } public int Index { get; set; } public Edge(int e, IPoint p, IPoint q) { Index = e; P = p; Q = q; } } internal struct Point : IPoint { public double X { get; set; } public double Y { get; set; } public Point(double x, double y) { X = x; Y = y; } public override string ToString() { return $"{X},{Y}"; } } internal struct Triangle : ITriangle { public int Index { get; set; } public IEnumerable<IPoint> Points { get; set; } public Triangle(int t, IEnumerable<IPoint> points) { Points = points; Index = t; } } internal struct VoronoiCell : IVoronoiCell { public IPoint[] Points { get; set; } public int Index { get; set; } public VoronoiCell(int triangleIndex, IPoint[] points) { Points = points; Index = triangleIndex; } } } namespace JetBrains.Annotations { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.GenericParameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class CanBeNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.Delegate | AttributeTargets.GenericParameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class NotNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class ItemNotNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class ItemCanBeNullAttribute : Attribute { } [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Delegate)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class StringFormatMethodAttribute : Attribute { [NotNull] public string FormatParameterName { get; } public StringFormatMethodAttribute([NotNull] string formatParameterName) { FormatParameterName = formatParameterName; } } [AttributeUsage(AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class StructuredMessageTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class ValueProviderAttribute : Attribute { [NotNull] public string Name { get; } public ValueProviderAttribute([NotNull] string name) { Name = name; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class ValueRangeAttribute : Attribute { public object From { get; } public object To { get; } public ValueRangeAttribute(long from, long to) { From = from; To = to; } public ValueRangeAttribute(ulong from, ulong to) { From = from; To = to; } public ValueRangeAttribute(long value) { From = (To = value); } public ValueRangeAttribute(ulong value) { From = (To = value); } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Delegate)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class NonNegativeValueAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class InvokerParameterNameAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute { [CanBeNull] public string ParameterName { get; } public NotifyPropertyChangedInvocatorAttribute() { } public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) { ParameterName = parameterName; } } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class ContractAnnotationAttribute : Attribute { [NotNull] public string Contract { get; } public bool ForceFullStates { get; } public ContractAnnotationAttribute([NotNull] string contract) : this(contract, forceFullStates: false) { } public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) { Contract = contract; ForceFullStates = forceFullStates; } } [AttributeUsage(AttributeTargets.All)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class LocalizationRequiredAttribute : Attribute { public bool Required { get; } public LocalizationRequiredAttribute() : this(required: true) { } public LocalizationRequiredAttribute(bool required) { Required = required; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class CannotApplyEqualityOperatorAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class DefaultEqualityUsageAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] [BaseTypeRequired(typeof(Attribute))] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class BaseTypeRequiredAttribute : Attribute { [NotNull] public Type BaseType { get; } public BaseTypeRequiredAttribute([NotNull] Type baseType) { BaseType = baseType; } } [AttributeUsage(AttributeTargets.All)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class UsedImplicitlyAttribute : Attribute { public ImplicitUseKindFlags UseKindFlags { get; } public ImplicitUseTargetFlags TargetFlags { get; } public string Reason { get; set; } public UsedImplicitlyAttribute() : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) : this(useKindFlags, ImplicitUseTargetFlags.Default) { } public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) : this(ImplicitUseKindFlags.Default, targetFlags) { } public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) { UseKindFlags = useKindFlags; TargetFlags = targetFlags; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter | AttributeTargets.GenericParameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class MeansImplicitUseAttribute : Attribute { [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; } [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; } public MeansImplicitUseAttribute() : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) { } public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) : this(useKindFlags, ImplicitUseTargetFlags.Default) { } public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) : this(ImplicitUseKindFlags.Default, targetFlags) { } public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) { UseKindFlags = useKindFlags; TargetFlags = targetFlags; } } [Flags] internal enum ImplicitUseKindFlags { Default = 7, Access = 1, Assign = 2, InstantiatedWithFixedConstructorSignature = 4, InstantiatedNoFixedConstructorSignature = 8 } [Flags] internal enum ImplicitUseTargetFlags { Default = 1, Itself = 1, Members = 2, WithInheritors = 4, WithMembers = 3 } [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] [AttributeUsage(AttributeTargets.All, Inherited = false)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class PublicAPIAttribute : Attribute { [CanBeNull] public string Comment { get; } public PublicAPIAttribute() { } public PublicAPIAttribute([NotNull] string comment) { Comment = comment; } } [AttributeUsage(AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class InstantHandleAttribute : Attribute { public bool RequireAwait { get; set; } } [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class PureAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class MustUseReturnValueAttribute : Attribute { [CanBeNull] public string Justification { get; } public bool IsFluentBuilderMethod { get; set; } public MustUseReturnValueAttribute() { } public MustUseReturnValueAttribute([NotNull] string justification) { Justification = justification; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class MustDisposeResourceAttribute : Attribute { public bool Value { get; } public MustDisposeResourceAttribute() { Value = true; } public MustDisposeResourceAttribute(bool value) { Value = value; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class HandlesResourceDisposalAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RequireStaticDelegateAttribute : Attribute { public bool IsError { get; set; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.GenericParameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class ProvidesContextAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class PathReferenceAttribute : Attribute { [CanBeNull] public string BasePath { get; } public PathReferenceAttribute() { } public PathReferenceAttribute([NotNull][PathReference] string basePath) { BasePath = basePath; } } [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class SourceTemplateAttribute : Attribute { public SourceTemplateTargetExpression Target { get; set; } } internal enum SourceTemplateTargetExpression { Inner, Outer } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class MacroAttribute : Attribute { [CanBeNull] public string Expression { get; set; } public int Editable { get; set; } [CanBeNull] public string Target { get; set; } } [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.ReturnValue)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class CollectionAccessAttribute : Attribute { public CollectionAccessType CollectionAccessType { get; } public CollectionAccessAttribute(CollectionAccessType collectionAccessType) { CollectionAccessType = collectionAccessType; } } [Flags] internal enum CollectionAccessType { None = 0, Read = 1, ModifyExistingContent = 2, UpdatedContent = 6 } [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AssertionMethodAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AssertionConditionAttribute : Attribute { public AssertionConditionType ConditionType { get; } public AssertionConditionAttribute(AssertionConditionType conditionType) { ConditionType = conditionType; } } internal enum AssertionConditionType { IS_TRUE, IS_FALSE, IS_NULL, IS_NOT_NULL } [Obsolete("Use [ContractAnnotation('=> halt')] instead")] [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class TerminatesProgramAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class LinqTunnelAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class NoEnumerationAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RegexPatternAttribute : Attribute { } internal enum InjectedLanguage { CSS, HTML, JAVASCRIPT, JSON, XML } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.ReturnValue)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class LanguageInjectionAttribute : Attribute { public InjectedLanguage InjectedLanguage { get; } [CanBeNull] public string InjectedLanguageName { get; } [CanBeNull] public string Prefix { get; set; } [CanBeNull] public string Suffix { get; set; } public LanguageInjectionAttribute(InjectedLanguage injectedLanguage) { InjectedLanguage = injectedLanguage; } public LanguageInjectionAttribute([NotNull] string injectedLanguage) { InjectedLanguageName = injectedLanguage; } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Interface, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class NoReorderAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface, AllowMultiple = true, Inherited = false)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class CodeTemplateAttribute : Attribute { public string SearchTemplate { get; } public string Message { get; set; } public string ReplaceTemplate { get; set; } public string ReplaceMessage { get; set; } public bool FormatAfterReplace { get; set; } = true; public bool MatchSimilarConstructs { get; set; } public bool ShortenReferences { get; set; } public string SuppressionKey { get; set; } public CodeTemplateAttribute(string searchTemplate) { SearchTemplate = searchTemplate; } } [AttributeUsage(AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class IgnoreSpellingAndGrammarErrorsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspChildControlTypeAttribute : Attribute { [NotNull] public string TagName { get; } [NotNull] public Type ControlType { get; } public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) { TagName = tagName; ControlType = controlType; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspDataFieldAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspDataFieldsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMethodPropertyAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspRequiredAttributeAttribute : Attribute { [NotNull] public string Attribute { get; } public AspRequiredAttributeAttribute([NotNull] string attribute) { Attribute = attribute; } } [AttributeUsage(AttributeTargets.Property)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspTypePropertyAttribute : Attribute { public bool CreateConstructorReferences { get; } public AspTypePropertyAttribute(bool createConstructorReferences) { CreateConstructorReferences = createConstructorReferences; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcAreaViewComponentViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcAreaViewComponentViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcAreaViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcMasterLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcMasterLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcPartialViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcViewComponentViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcViewComponentViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcViewLocationFormatAttribute : Attribute { [NotNull] public string Format { get; } public AspMvcViewLocationFormatAttribute([NotNull] string format) { Format = format; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcActionAttribute : Attribute { [CanBeNull] public string AnonymousProperty { get; } public AspMvcActionAttribute() { } public AspMvcActionAttribute([NotNull] string anonymousProperty) { AnonymousProperty = anonymousProperty; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcAreaAttribute : Attribute { [CanBeNull] public string AnonymousProperty { get; } public AspMvcAreaAttribute() { } public AspMvcAreaAttribute([NotNull] string anonymousProperty) { AnonymousProperty = anonymousProperty; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcControllerAttribute : Attribute { [CanBeNull] public string AnonymousProperty { get; } public AspMvcControllerAttribute() { } public AspMvcControllerAttribute([NotNull] string anonymousProperty) { AnonymousProperty = anonymousProperty; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcMasterAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcModelTypeAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcPartialViewAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcSuppressViewErrorAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcDisplayTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcEditorTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcViewAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcViewComponentAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcViewComponentViewAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMvcActionSelectorAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RouteTemplateAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RouteParameterConstraintAttribute : Attribute { [NotNull] public string ConstraintName { get; } [CanBeNull] public Type ProposedType { get; set; } public RouteParameterConstraintAttribute([NotNull] string constraintName) { ConstraintName = constraintName; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class UriStringAttribute : Attribute { [CanBeNull] public string HttpVerb { get; } public UriStringAttribute() { } public UriStringAttribute(string httpVerb) { HttpVerb = httpVerb; } } [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspRouteConventionAttribute : Attribute { [CanBeNull] public string PredefinedPattern { get; } public AspRouteConventionAttribute() { } public AspRouteConventionAttribute(string predefinedPattern) { PredefinedPattern = predefinedPattern; } } [AttributeUsage(AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspDefaultRouteValuesAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspRouteValuesConstraintsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspRouteOrderAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspRouteVerbsAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspAttributeRoutingAttribute : Attribute { public string HttpVerb { get; set; } } [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMinimalApiDeclarationAttribute : Attribute { public string HttpVerb { get; set; } } [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMinimalApiGroupAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMinimalApiHandlerAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class AspMinimalApiImplicitEndpointDeclarationAttribute : Attribute { public string HttpVerb { get; set; } public string RouteTemplate { get; set; } public Type BodyType { get; set; } public string QueryParameters { get; set; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class HtmlElementAttributesAttribute : Attribute { [CanBeNull] public string Name { get; } public HtmlElementAttributesAttribute() { } public HtmlElementAttributesAttribute([NotNull] string name) { Name = name; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class HtmlAttributeValueAttribute : Attribute { [NotNull] public string Name { get; } public HtmlAttributeValueAttribute([NotNull] string name) { Name = name; } } [AttributeUsage(AttributeTargets.Method | AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RazorSectionAttribute : Attribute { } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RazorImportNamespaceAttribute : Attribute { [NotNull] public string Name { get; } public RazorImportNamespaceAttribute([NotNull] string name) { Name = name; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RazorInjectionAttribute : Attribute { [NotNull] public string Type { get; } [NotNull] public string FieldName { get; } public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) { Type = type; FieldName = fieldName; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RazorDirectiveAttribute : Attribute { [NotNull] public string Directive { get; } public RazorDirectiveAttribute([NotNull] string directive) { Directive = directive; } } [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RazorPageBaseTypeAttribute : Attribute { [NotNull] public string BaseType { get; } [CanBeNull] public string PageName { get; } public RazorPageBaseTypeAttribute([NotNull] string baseType) { BaseType = baseType; } public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) { BaseType = baseType; PageName = pageName; } } [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RazorHelperCommonAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RazorLayoutAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RazorWriteLiteralMethodAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RazorWriteMethodAttribute : Attribute { } [AttributeUsage(AttributeTargets.Parameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class RazorWriteMethodParameterAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class XamlItemsControlAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class XamlItemBindingOfItemsControlAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class XamlItemStyleOfItemsControlAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class XamlOneWayBindingModeByDefaultAttribute : Attribute { } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class XamlTwoWayBindingModeByDefaultAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Interface, AllowMultiple = true)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class TestSubjectAttribute : Attribute { [NotNull] public Type Subject { get; } public TestSubjectAttribute([NotNull] Type subject) { Subject = subject; } } [AttributeUsage(AttributeTargets.GenericParameter)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class MeansTestSubjectAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Interface)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class CqrsCommandAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Interface)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class CqrsQueryAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Interface)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class CqrsCommandHandlerAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Interface)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class CqrsQueryHandlerAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Interface)] [Conditional("JETBRAINS_ANNOTATIONS")] internal sealed class CqrsExcludeFromAnalysisAttribute : Attribute { } }