static IEnumerator GeneratePoints(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { var pointRandomizer = new System.Random(data.Randomizer.Next()); data.Points = new List <Vector2f>(); for (var i = 0; i < options.PointCount; i++) { data.Points.Add(new Vector2f( pointRandomizer.NextFloat(0, 1), pointRandomizer.NextFloat(0, 1))); yield return(null); } }
static IEnumerator GenerateHeightMap(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { var heightMapRandom = new System.Random(data.Randomizer.Next()); var queue = new Queue <Corner>(); for (int c = 0; c < graph.Corners.Count; c++) { var corner = graph.Corners[c]; //Borders are always ocean if (corner.IsBorder) { corner.Elevation = 0; //Add all the borders to the processing queue and work inward queue.Enqueue(corner); } else { //treating PositiveInfinity as unset corner.Elevation = float.PositiveInfinity; } yield return(null); } while (queue.Any()) { var corner = queue.Dequeue(); foreach (var neighbor in corner.Neighbors) { //always increase slightly to prevent reprocessing var newElevation = corner.Elevation + 0.01f; //increase by random amount if travelling over land if (!corner.Biome.IsWater && !neighbor.Biome.IsWater) { newElevation += heightMapRandom.NextFloat(1, 2); } //TODO: find way to improve this, could cause a lot of reprocessing if (newElevation < neighbor.Elevation) { neighbor.Elevation = newElevation; queue.Enqueue(neighbor); } } yield return(null); } }
void GenerateTerrain(Graph graph, WorldGeneratorData data, WorldGeneratorOptions graphOptions) { graph.TerrainData = new List <TerrainData>(); var step = 1f / graphOptions.ChunkCount; for (var x = 0f; x <= 0.999f; x += step) { for (var y = 0f; y <= 0.999f; y += step) { var terrainData = new TerrainData(); terrainData.size = new Vector3(graphOptions.TerrainResolution, graphOptions.TerrainResolution, graphOptions.TerrainResolution); graph.TerrainData.Add(terrainData); } } }
static IEnumerator CalculateDownslopes(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { foreach (var corner in graph.Corners) { corner.Downslope = corner; foreach (var neighbor in corner.Neighbors) { if (neighbor.Elevation < corner.Elevation) { corner.Downslope = neighbor; } yield return(null); } } }
static IEnumerator AssignBiomes(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { graph.RegionsByBiome = new Dictionary <Biome, List <Region> >(); graph.CoastalRegions = new List <Region>(); foreach (var region in graph.Regions) { if (region.Biome != Biome.Ocean && region.Biome != Biome.Lake) { if (region.Elevation > options.MountainFactor) { if (region.Moisture > options.SnowFactor) { region.Biome = Biome.Snow; } else { region.Biome = Biome.Mountain; } } else { if (region.Moisture > options.ForestFactor) { region.Biome = Biome.Forest; } else if (region.Moisture < options.DesertFactor) { region.Biome = Biome.Desert; } else { region.Biome = Biome.Plains; } } if (region.IsCoast) { graph.CoastalRegions.Add(region); } } graph.RegionsByBiome.GetOrAdd(region.Biome, b => new List <Region>()).Add(region); yield return(null); } }
static IEnumerator CalculateWatersheds(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { LinkedList <Corner> corners = new LinkedList <Corner>(); foreach (var corner in graph.Corners) { if (corner.Biome == Biome.Ocean || corner.IsCoast) { corner.Watershed = corner; corner.WatershedSize = 0; } else { corner.Watershed = corner.Downslope; corner.WatershedSize = 1; corners.AddLast(corner); } yield return(null); } for (var i = 0; i < 100; i++) { var corner = corners.First; while (corner != null) { var nextCorner = corner.Next; if (corner.Value.Watershed.Biome == Biome.Ocean || corner.Value.Watershed.IsCoast) { corners.Remove(corner); } else { corner.Value.Watershed = corner.Value.Watershed.Downslope; corner.Value.WatershedSize++; } corner = nextCorner; } if (corners.Count == 0) { break; } yield return(null); } }
public static IEnumerator Generate(WorldGeneratorOptions options, Action <Graph> callback) { var data = new WorldGeneratorData { Randomizer = new System.Random(options.Seed), VoronoiBounds = new Rectf(0, 0, 1, 1) }; var graph = new Graph { }; foreach (var step in Steps) { var enumerator = step(graph, data, options); while (enumerator.MoveNext()) { yield return(enumerator.Current); } } callback(graph); }
static IEnumerator GenerateIsland(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { var islandRandomizer = new System.Random(data.Randomizer.Next()); var bumps = islandRandomizer.Next(1, 6); var startAngle = islandRandomizer.NextDouble(0, 2 * Math.PI); var dipAngle = islandRandomizer.NextDouble(0, 2 * Math.PI); var dipWidth = islandRandomizer.NextDouble(0.2, 0.7); for (int c = 0; c < graph.Corners.Count; c++) { var corner = graph.Corners[c]; if (corner.IsBorder) { corner.Biome = Biome.Ocean; continue; } var x = (corner.Point.x - .5) * 2; var y = (corner.Point.y - .5) * 2; var angle = Math.Atan2(y, x); var length = Math.Sqrt(x * x + y * y); //https://www.desmos.com/calculator/wjs0xfogfe var r1 = 0.5 + 0.40 * Math.Sin(startAngle + bumps * angle + Math.Cos((bumps + 3) * angle)); var r2 = 0.7 - 0.20 * Math.Sin(startAngle + bumps * angle - Math.Sin((bumps + 2) * angle)); if (Math.Abs(angle - dipAngle) < dipWidth || Math.Abs(angle - dipAngle + 2 * Math.PI) < dipWidth || Math.Abs(angle - dipAngle - 2 * Math.PI) < dipWidth) { r1 = r2 = 0.2; } if (!(length < r1 || (length > r1 * options.IslandFactor && length < r2))) { corner.Biome = Biome.Lake; } yield return(null); } }
static IEnumerator CreateRivers(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { var riverRandomizer = new System.Random(data.Randomizer.Next()); for (int i = 0; i < options.PointCount * options.RiverFactor; i++) { yield return(null); var corner = graph.Corners[riverRandomizer.Next(0, graph.Corners.Count - 1)]; if (corner.Biome == Biome.Ocean || corner.Elevation < 0.3 || corner.Elevation > 0.9) { continue; } while (!corner.IsCoast && corner != corner.Downslope) { var edge = corner.Edges.First(e => e.OtherCorner(corner) == corner.Downslope); edge.RiverCount++; corner.RiverCount++; corner.Downslope.RiverCount++; corner = corner.Downslope; } } }
static IEnumerator ImproveCorners(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { for (int c = 0; c < graph.Corners.Count; c++) { var corner = graph.Corners[c]; if (corner.IsBorder) { continue; } corner.Point.x = 0; corner.Point.y = 0; for (int r = 0; r < corner.Regions.Count; r++) { Region region = corner.Regions[r]; corner.Point.x += region.Center.x; corner.Point.y += region.Center.y; } corner.Point.x /= corner.Regions.Count; corner.Point.y /= corner.Regions.Count; yield return(null); } }
static IEnumerator BuildVoronoiDiagram(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { data.Voronoi = new Voronoi(data.Points, data.VoronoiBounds, options.Normality); yield return(null); }
static IEnumerator GenerateCities(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { void recurse(HashSet <Region> touchedRegions, List <Region> cluster, Region region, Biome biome) { if (region.Biome != biome) { return; } if (touchedRegions.Contains(region)) { return; } cluster.Add(region); touchedRegions.Add(region); for (int i = 0; i < region.Neighbors.Count; i++) { var neighbor = region.Neighbors[i]; recurse(touchedRegions, cluster, neighbor, biome); } } var cityRandomizer = new System.Random(data.Randomizer.Next()); graph.Cities = new List <City>(); graph.Cities.Add(new City { Region = graph.CoastalRegions[cityRandomizer.Next(0, graph.CoastalRegions.Count)] }); for (int i = 0; i < graph.RegionsByBiome.Count; i++) { var biome = graph.RegionsByBiome.Keys.ElementAt(i); if (biome == Biome.Ocean || biome == Biome.Lake) { continue; } var regions = graph.RegionsByBiome[biome]; HashSet <Region> touchedRegions = new HashSet <Region>(); List <Region> cluster = null; for (int j = 0; j < regions.Count; j++) { var region = regions[j]; if (touchedRegions.Contains(region)) { continue; } var newCluster = new List <Region>(); recurse(touchedRegions, newCluster, region, region.Biome); if (cluster == null || cluster.Count < newCluster.Count) { cluster = newCluster; } yield return(null); } if (cluster == null) { continue; } cluster = cluster.OrderBy(r => r.Center.x * r.Center.y).ToList(); var midpoint = cluster[0].Center + cluster[cluster.Count - 1].Center; midpoint = new Vector2f(midpoint.x / 2, midpoint.y / 2); var midRegion = cluster.OrderBy(r => Mathf.Pow(midpoint.x - r.Center.x, 2) + Mathf.Pow(midpoint.y - r.Center.y, 2)).First(); graph.Cities.Add(new City { Region = midRegion }); yield return(null); } }
static IEnumerator AssignCoastalRegions(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { var oceanQueue = new Queue <Region>(); for (int r = 0; r < graph.Regions.Count; r++) { Region region = graph.Regions[r]; var waterCount = 0; for (int c = 0; c < region.Corners.Count; c++) { Corner corner = region.Corners[c]; if (corner.IsBorder) { region.IsBorder = true; region.Biome = Biome.Ocean; oceanQueue.Enqueue(region); continue; } if (corner.Biome.IsWater) { waterCount++; } yield return(null); } if (waterCount >= region.Corners.Count() * options.LakeFactor) { region.Biome = Biome.Lake; } } //Flood fill ocean from borders while (oceanQueue.Any()) { var region = oceanQueue.Dequeue(); for (var n = 0; n < region.Neighbors.Count; n++) { var other = region.Neighbors[n]; if (other.Biome == Biome.Lake) { other.Biome = Biome.Ocean; oceanQueue.Enqueue(other); } yield return(null); } } for (int r = 0; r < graph.Regions.Count; r++) { Region region = graph.Regions[r]; region.IsCoast = false; var ocean = false; var land = false; foreach (var neighbor in region.Neighbors) { if (neighbor.Biome == Biome.Ocean) { ocean = true; } else { land = true; } if (ocean && land) { region.IsCoast = true; continue; } } yield return(null); } for (int c = 0; c < graph.Corners.Count; c++) { Corner corner = graph.Corners[c]; corner.IsCoast = false; var ocean = false; var land = false; foreach (var region in corner.Regions) { if (region.Biome == Biome.Ocean) { ocean = true; } else { land = true; } if (ocean && land) { break; } yield return(null); } if (ocean) { if (land) { corner.IsCoast = true; } if (corner.Biome == Biome.Lake) { corner.Biome = Biome.Ocean; } } yield return(null); } }
static IEnumerator AssignRegionMoisture(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { foreach (var region in graph.Regions) { region.Moisture = 0f; foreach (var corner in region.Corners) { region.Moisture += corner.Moisture; yield return(null); } region.Moisture /= region.Corners.Count; } }
static IEnumerator RedistributeMoisture(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { var corners = graph.Corners.Where(c => c.Biome != Biome.Ocean).OrderBy(c => c.Moisture).ToList(); var count = corners.Count - 1; for (int i = 0; i <= count; i++) { corners[i].Moisture = (float)i / count; yield return(null); } }
static IEnumerator RedistributeElevations(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { // SCALE_FACTOR increases the mountain area. At 1.0 the maximum // elevation barely shows up on the map, so we set it to 1.1. var SCALE_FACTOR = 1.1f; var corners = graph.Corners.Where(c => c.Biome != Biome.Ocean).OrderBy(c => c.Elevation).ToList(); var count = corners.Count - 1; for (int i = 0; i <= count; i++) { var y = (float)i / count; var x = Mathf.Sqrt(SCALE_FACTOR) - Mathf.Sqrt(SCALE_FACTOR * (1 - y)); if (x > 1f) { x = 1f; } corners[i].Elevation = x; yield return(null); } for (int c = 0; c < graph.Corners.Count; c++) { var corner = graph.Corners[c]; if (corner.Biome == Biome.Ocean || corner.IsCoast) { corner.Elevation = 0; } yield return(null); } }
static IEnumerator AssignCornerMoisture(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { Queue <Corner> cornerQueue = new Queue <Corner>(); foreach (var corner in graph.Corners) { if (corner.RiverCount > 0 || corner.Biome == Biome.Lake) { corner.Moisture = corner.RiverCount > 0 ? Mathf.Min(3f, .2f * corner.RiverCount) : 1f; cornerQueue.Enqueue(corner); } else { corner.Moisture = 0f; } yield return(null); } while (cornerQueue.Any()) { var corner = cornerQueue.Dequeue(); foreach (var neighbor in corner.Neighbors) { //if (neighbor.Biome == Biome.Ocean || neighbor.IsCoast) continue; var newMoisture = corner.Moisture * .9f; if (newMoisture > neighbor.Moisture) { neighbor.Moisture = newMoisture; cornerQueue.Enqueue(corner); } yield return(null); } } foreach (var corner in graph.Corners) { if (corner.Biome == Biome.Ocean || corner.IsCoast) { corner.Moisture = 1f; } yield return(null); } }
static IEnumerator AssignRegionElevations(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { foreach (var region in graph.Regions) { var sum = 0f; foreach (var corner in region.Corners) { sum += corner.Elevation; yield return(null); } region.Elevation = sum / region.Corners.Count; yield return(null); } }
static IEnumerator BuildRegionGraph(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { var regions = new ProximityMap <Region>(10, 1); var corners = new ProximityMap <Corner>(10, 1); var edges = new List <Edge>(); var coords = data.Voronoi.SiteCoords(); for (int c = 0; c < coords.Count; c++) { var coord = coords[c]; var site = data.Voronoi.SitesIndexedByLocation[coord]; var region = new Region { Center = coord }; var points = site.Region(data.VoronoiBounds); for (int p = 0; p < points.Count; p++) { var point = points[p]; var corner = GetOrAddCorner(corners, point); corner.Regions.Add(region); region.Corners.Add(corner); } regions.Add(region.Center, region); yield return(null); } csDelaunay.Edge voronoiEdge; for (var e = 0; e < data.Voronoi.Edges.Count; e++) { voronoiEdge = data.Voronoi.Edges[e]; if (!voronoiEdge.Visible() || voronoiEdge.ClippedEnds == null) { continue; } var leftCorner = GetOrAddCorner(corners, voronoiEdge.ClippedEnds[LR.LEFT]); var rightCorner = GetOrAddCorner(corners, voronoiEdge.ClippedEnds[LR.RIGHT]); var edge = new Edge(); edge.LeftCorner = leftCorner; edge.LeftCorner.Edges.Add(edge); edge.RightCorner = rightCorner; edge.RightCorner.Edges.Add(edge); if (voronoiEdge.LeftSite != null && regions.Find(voronoiEdge.LeftSite.Coord, out var leftRegion, false)) { edge.LeftRegion = leftRegion.Value; edge.LeftRegion.Edges.Add(edge); } if (voronoiEdge.RightSite != null && regions.Find(voronoiEdge.RightSite.Coord, out var rightRegion, false)) { edge.RightRegion = rightRegion.Value; edge.RightRegion.Edges.Add(edge); } if (edge.LeftRegion != null && edge.RightRegion != null) { edge.LeftRegion.Neighbors.Add(edge.RightRegion); edge.RightRegion.Neighbors.Add(edge.LeftRegion); } edge.LeftCorner.Neighbors.Add(edge.RightCorner); edge.RightCorner.Neighbors.Add(edge.LeftCorner); edges.Add(edge); yield return(null); } graph.Regions = new List <Region>(); for (int r = 0; r < regions.Elements.Count; r++) { graph.Regions.Add(regions.Elements[r].Value); } graph.Corners = new List <Corner>(); for (int c = 0; c < corners.Elements.Count; c++) { graph.Corners.Add(corners.Elements[c].Value); } graph.Edges = edges; }
static IEnumerator DisposeVoronoi(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options) { data.Voronoi.Dispose(); data.Voronoi = null; yield return(null); }