예제 #1
0
        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);
            }
        }
예제 #2
0
        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);
            }
        }
예제 #3
0
        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);
                }
            }
        }
예제 #4
0
        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);
                }
            }
        }
예제 #5
0
        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);
            }
        }
예제 #6
0
        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);
            }
        }
예제 #7
0
        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);
        }
예제 #8
0
        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);
            }
        }
예제 #9
0
        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;
                }
            }
        }
예제 #10
0
        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);
            }
        }
예제 #11
0
 static IEnumerator BuildVoronoiDiagram(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options)
 {
     data.Voronoi = new Voronoi(data.Points, data.VoronoiBounds, options.Normality);
     yield return(null);
 }
예제 #12
0
        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);
            }
        }
예제 #13
0
        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);
            }
        }
예제 #14
0
        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;
            }
        }
예제 #15
0
        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);
            }
        }
예제 #16
0
        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);
            }
        }
예제 #17
0
        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);
            }
        }
예제 #18
0
        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);
            }
        }
예제 #19
0
        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;
        }
예제 #20
0
 static IEnumerator DisposeVoronoi(Graph graph, WorldGeneratorData data, WorldGeneratorOptions options)
 {
     data.Voronoi.Dispose();
     data.Voronoi = null;
     yield return(null);
 }