예제 #1
0
    private void AssignOceanCoastAndLand()
    {
        Queue <CellCenter> queue = new Queue <CellCenter>();
        int numWater             = 0;

        //Set the cell to water / ocean / border
        foreach (var center in cells)
        {
            numWater = 0;

            foreach (var corner in center.cellCorners)
            {
                if (corner.isBorder)                 //If a corner connected to this cell is at the map border, then this cell is a ocean and the corner itself is water
                {
                    center.isBorder = true;
                    center.isOcean  = true;
                    corner.isWater  = true;
                    queue.Enqueue(center);
                }

                if (corner.isWater)
                {
                    numWater += 1;
                }
            }

            //If the amount of corners on this cell is grater than the defined threshold, this cell is water
            center.isWater = center.isOcean || numWater >= center.cellCorners.Count * LAKE_THRESHOLD;
        }

        //Every cell around a border must be a ocean too, and we loop thought the neighbors until can't find more water (at which case, the queue would be empty)
        while (queue.Count > 0)
        {
            CellCenter c = queue.Dequeue();

            foreach (var n in c.neighborCells)
            {
                if (n.isWater && !n.isOcean)
                {
                    n.isOcean = true;
                    queue.Enqueue(n);                     //If this neighbor is a ocean, we add it to the queue so wwe can check its neighbors
                }
            }
        }

        //Set the Coast cells based on the neighbors. If a cell has at least one ocean and one land neighbor, then the cell is a coast
        foreach (var cell in cells)
        {
            int numOcean = 0;
            int numLand  = 0;

            foreach (var n in cell.neighborCells)
            {
                numOcean += n.isOcean ? 1 : 0;
                numLand  += !n.isWater ? 1 : 0;
            }

            cell.isCoast = numOcean > 0 && numLand > 0;
        }
    }
    private void DrawElevation()
    {
        Color low   = Color.black;
        Color high  = Color.white;
        Color water = Color.blue * 0.5f;

        water.a = 1;
        Color waterDeep = Color.blue * 0.1f;

        waterDeep.a = 1;

        for (int x = 0; x < resolution.x; x++)
        {
            for (int y = 0; y < resolution.y; y++)
            {
                CellCenter c = generator.cells[cellIDs[x + y * resolution.y]];

                if (c.elevation < 0)
                {
                    texColors[x, y] = Color.Lerp(waterDeep, water, 1 + c.elevation);
                }
                else
                {
                    texColors[x, y] = Color.Lerp(low, high, c.elevation);
                }
            }
        }
    }
    private void DrawMapBorders()
    {
        for (int x = 0; x < resolution.x; x++)
        {
            for (int y = 0; y < resolution.y; y++)
            {
                CellCenter c = generator.cells[cellIDs[x + y * resolution.y]];

                if (c.isBorder)
                {
                    texColors[x, y] = Color.red;
                }
            }
        }

        Color borderCorner = new Color(1, .5f, .5f);

        foreach (var corner in generator.corners)
        {
            if (corner.isBorder)
            {
                DrawGraphPoint(corner, borderCorner, POINT_SIZE * 2);
            }
        }
    }
    private void DrawIslands()
    {
        Dictionary <int, Color> islandColors = new Dictionary <int, Color>();

        for (int i = 0; i < generator.islands.Count; i++)
        {
            islandColors.Add(generator.islands[i][0].islandID, Color.HSVToRGB((i * (360f / generator.islands.Count)) / 360f, Random.Range(.5f, 1), 1));
        }

        for (int x = 0; x < resolution.x; x++)
        {
            for (int y = 0; y < resolution.y; y++)
            {
                int        currentCellID = cellIDs[x + y * resolution.y];
                CellCenter c             = generator.cells[currentCellID];

                if (c.islandID < 0)
                {
                    texColors[x, y] = Color.black;
                }
                else
                {
                    texColors[x, y] = islandColors[c.islandID];
                }
            }
        }
    }
    private int[] GetClosestCenterForPixels()
    {
        //Send the data to the compute shader so the GPU can do the hard work
        CellData[]    cellData             = new CellData[generator.cells.Count];
        ComputeBuffer cellDataBuffer       = new ComputeBuffer(generator.cells.Count, sizeof(int) * 2);
        ComputeBuffer cellIdByPixeldBuffer = new ComputeBuffer(resolution.x * resolution.y, sizeof(int));         //This is the buffer we're gonna read from

        for (int i = 0; i < cellData.Length; i++)
        {
            CellCenter c = generator.cells[i];
            cellData[i] = new CellData()
            {
                position = MapGraphCoordToTextureCoords(c.position.x, c.position.y)
            };
        }

        cellDataBuffer.SetData(cellData);
        cellIdByPixeldBuffer.SetData(new int[resolution.x * resolution.y]);         //we pass an empty array, since we just want to retrieve this data
        computeShader.SetBuffer(findClosestCellKernelIndex, "_CellData", cellDataBuffer);
        computeShader.SetBuffer(findClosestCellKernelIndex, "_CellIDByPixel", cellIdByPixeldBuffer);
        computeShader.SetInt("_Resolution", resolution.x);

        computeShader.Dispatch(findClosestCellKernelIndex, resolution.x / 8, resolution.y / 8, 1);

        //Get the result data back from the GPU
        int[] centersIDs = new int[resolution.x * resolution.y];
        cellIdByPixeldBuffer.GetData(centersIDs);

        cellDataBuffer?.Dispose();
        cellIdByPixeldBuffer?.Dispose();

        return(centersIDs);
    }
    private CellCenter GetClosestCenterFromPoint(Vector2 point)
    {
        float      smallestDistance = float.MaxValue;
        CellCenter c = null;

        foreach (var center in generator.cells)
        {
            float d = Vector2.Distance(point, center.position);
            if (d < smallestDistance)
            {
                smallestDistance = d;
                c = center;
            }
        }

        return(c);
    }
    private void DrawBiomes()
    {
        for (int x = 0; x < resolution.x; x++)
        {
            for (int y = 0; y < resolution.y; y++)
            {
                int        currentCellID = cellIDs[x + y * resolution.y];
                CellCenter c             = generator.cells[currentCellID];

                BiomeColor biome = biomes.FirstOrDefault(b => b.biome == c.biome);

                if (biome != null)
                {
                    texColors[x, y] = biome.color;
                }
                else
                {
                    texColors[x, y] = Color.black;
                }
            }
        }
    }
    private void DrawWaterAndLand()
    {
        Color ocean = new Color(.1f, .1f, .5f);
        Color water = new Color(.5f, .5f, .7f);
        Color land  = new Color(.7f, .7f, .5f);
        Color coast = new Color(.5f, .5f, .3f);

        for (int x = 0; x < resolution.x; x++)
        {
            for (int y = 0; y < resolution.y; y++)
            {
                int        currentCellID = cellIDs[x + y * resolution.y];
                CellCenter c             = generator.cells[currentCellID];

                if (c.isWater)
                {
                    if (c.isOcean)
                    {
                        texColors[x, y] = ocean;
                    }
                    else
                    {
                        texColors[x, y] = water;
                    }
                }
                else
                {
                    if (c.isCoast)
                    {
                        texColors[x, y] = coast;
                    }
                    else
                    {
                        texColors[x, y] = land;
                    }
                }
            }
        }
    }
    private void DrawSelected()
    {
        CellCenter c = generator.cells[selectedID];

        DrawGraphPoint(c, Color.green, POINT_SIZE * 3, 2);

        for (int i = 0; i < c.neighborCells.Count; i++)
        {
            DrawGraphPoint(c.neighborCells[i], Color.magenta, POINT_SIZE * 2, 2);
        }

        for (int i = 0; i < c.cellCorners.Count; i++)
        {
            DrawGraphPoint(c.cellCorners[i], Color.yellow, POINT_SIZE * 2, 2);
        }

        for (int i = 0; i < c.borderEdges.Count; i++)
        {
            DrawGraphEdge(c.borderEdges[i], new Color(0, .5f, .5f), 1, false);
            DrawGraphEdge(c.borderEdges[i], Color.cyan, 1, true);
        }
    }
    private string GetInfoFromCell()
    {
        CellCenter c = generator.cells.First(x => x.index == selectedID);

        string info = $"CELL:\n";

        info += "\n- " + string.Join("\n- ",
                                     $"id: {c.index}",
                                     $"elevation: {c.elevation}",
                                     $"moisture: {c.moisture}",
                                     $"isCoast: {c.isCoast}",
                                     $"isWater: {c.isWater}",
                                     $"isOcean: {c.isOcean}",
                                     $"isBorder: {c.isBorder}"
                                     );

        info += "\n\nEDGES:\n";

        foreach (var edge in c.borderEdges)
        {
            info += "\n- " + string.Join("\n",
                                         $"id: {edge.index}"
                                         );
        }

        info += "\n\nCORNERS:\n";

        foreach (var corner in c.cellCorners)
        {
            info += "\n- " + string.Join("\n",
                                         $"id: {corner.index}",
                                         $"elevation: {corner.elevation}"
                                         );
        }

        return(info);
    }
    private void DrawMoisture()
    {
        Color water = new Color(.1f, .1f, .5f);
        Color wet   = new Color(0.25f, .39f, .2f);
        Color dry   = new Color(.8f, .7f, .5f);

        for (int x = 0; x < resolution.x; x++)
        {
            for (int y = 0; y < resolution.y; y++)
            {
                int        currentCellID = cellIDs[x + y * resolution.y];
                CellCenter c             = generator.cells[currentCellID];

                if (c.isWater)
                {
                    texColors[x, y] = water;
                }
                else
                {
                    texColors[x, y] = Color.Lerp(dry, wet, c.moisture);
                }
            }
        }
    }
예제 #12
0
    private void AssignMoisture()
    {
        HashSet <CellCenter> moistureSeeds = new HashSet <CellCenter>();       //we use a HashSet to guarantee we won't have duplicate values

        foreach (var edge in edges)
        {
            //Find all riverbanks (regions adjacent to rivers)
            if (edge.waterVolume > 0)
            {
                moistureSeeds.Add(edge.d0);
                moistureSeeds.Add(edge.d1);
            }

            //Find all lakeshores (regions adjacent to lakes)
            if ((edge.d0.isWater && !edge.d0.isOcean) || (edge.d1.isWater && !edge.d1.isOcean))
            {
                moistureSeeds.Add(edge.d0);
                moistureSeeds.Add(edge.d1);
            }
        }

        //Copy the hashset values to a queue
        Queue <CellCenter>      queue         = new Queue <CellCenter>(moistureSeeds);
        Dictionary <int, float> waterDistance = new Dictionary <int, float>();

        //Set the distance of each cell in the queue to 0, since they're the closest to a water source. Any other cell gets -1
        foreach (var cell in cells)
        {
            if (queue.Contains(cell))
            {
                waterDistance.Add(cell.index, 0);
            }
            else
            {
                waterDistance.Add(cell.index, -1);
            }
        }

        float maxDistance = 1;

        while (queue.Count > 0)
        {
            CellCenter currentCell = queue.Dequeue();

            foreach (var neighbor in currentCell.neighborCells)
            {
                if (!neighbor.isWater && waterDistance[neighbor.index] < 0)
                {
                    float newDistance = waterDistance[currentCell.index] + 1;
                    waterDistance[neighbor.index] = newDistance;

                    if (newDistance > maxDistance)
                    {
                        maxDistance = newDistance;
                    }

                    queue.Enqueue(neighbor);
                }
            }
        }

        //Normalize the moisture values
        foreach (var cell in cells)
        {
            cell.moisture = cell.isWater ? 1 : 1 - (waterDistance[cell.index] / maxDistance);
        }

        ////Redistribute moisture values evenly
        //List<CellCenter> land = cells.Where(x => !x.isWater).ToList();
        //land.Sort((x, y) =>
        //{
        //	if (x.moisture < y.moisture)
        //		return -1;

        //	if (x.moisture > y.moisture)
        //		return 1;

        //	return 0;
        //});

        //float minMoisture = 0;
        //float maxMoisture = 1;

        //for (int i = 0; i < land.Count; i++)
        //{
        //	land[i].moisture = minMoisture + (maxMoisture - minMoisture) * i / (land.Count - 1);
        //}
    }
예제 #13
0
    private void DetectIslands()
    {
        List <CellCenter> land = new List <CellCenter>();

        foreach (var cell in cells)
        {
            if (cell.isOcean)
            {
                cell.islandID = -1;                 //Ocean tiles doesn't have a island
            }
            else
            {
                land.Add(cell);
            }
        }

        islands = new List <List <CellCenter> >();

        for (int i = 0; i < land.Count; i++)
        {
            CellCenter currentCell = land[i];

            //Is the current cell in any island already?
            if (!islands.Any(x => x.Contains(currentCell)))
            {
                //If not, create a new island for it and add the current cell
                List <CellCenter> currentIsland = new List <CellCenter>();
                islands.Add(currentIsland);

                currentIsland.Add(currentCell);
                currentCell.islandID = islands.Count - 1;

                //Create a queue with the current cell. We check its neighbors to see if they are not an ocean tile. If not, check if it was already added to the current island.
                Queue <CellCenter> islandQueue = new Queue <CellCenter>();
                islandQueue.Enqueue(currentCell);

                while (islandQueue.Count > 0)
                {
                    currentCell = islandQueue.Dequeue();

                    foreach (var neighbor in currentCell.neighborCells)
                    {
                        if (!neighbor.isOcean && !currentIsland.Contains(neighbor))
                        {
                            islandQueue.Enqueue(neighbor);                             //Add the neighbor to the queue so we can then check its neighbors, until we can't find any neightbor that is either a ocean or was not added to the current island
                            currentIsland.Add(neighbor);
                            neighbor.islandID = islands.Count - 1;
                        }
                    }
                }
            }
        }

        if (singleIsland)
        {
            minIslandSize = islands.Max(x => x.Count);
        }

        //Remove all islands that have a lower cell count than the minimum size
        for (int i = 0; i < islands.Count; i++)
        {
            List <CellCenter> currentIsland = islands[i];

            if (currentIsland.Count < minIslandSize)
            {
                foreach (var cell in currentIsland)
                {
                    //We transform all cells in the islands we want to discard into ocean cells
                    cell.isWater  = true;
                    cell.isOcean  = true;
                    cell.islandID = -1;
                }

                //Remove the current island
                islands.RemoveAt(i);
                i--;
            }
        }
    }
예제 #14
0
    private void GenerateGraphs(List <Vector2> points)
    {
        //Generate the Voronoi
        Rectf   bounds  = new Rectf(0, 0, size.x, size.y);
        Voronoi voronoi = new Voronoi(points, bounds, relaxation);

        //Cell centers
        foreach (var site in voronoi.SitesIndexedByLocation)
        {
            CellCenter c = new CellCenter();
            c.index    = cells.Count;
            c.position = site.Key;

            cells.Add(c);
        }

        //Cell Corners
        foreach (var edge in voronoi.Edges)
        {
            //If the edge doesn't have clipped ends, it was not withing bounds
            if (edge.ClippedEnds == null)
            {
                continue;
            }

            if (!corners.Any(x => x.position == edge.ClippedEnds[LR.LEFT]))
            {
                CellCorner c = new CellCorner();
                c.index    = corners.Count;
                c.position = edge.ClippedEnds[LR.LEFT];
                c.isBorder = c.position.x == 0 || c.position.x == size.x || c.position.y == 0 || c.position.y == size.y;

                corners.Add(c);
            }

            if (!corners.Any(x => x.position == edge.ClippedEnds[LR.RIGHT]))
            {
                CellCorner c = new CellCorner();
                c.index    = corners.Count;
                c.position = edge.ClippedEnds[LR.RIGHT];
                c.isBorder = c.position.x == 0 || c.position.x == size.x || c.position.y == 0 || c.position.y == size.y;

                corners.Add(c);
            }
        }

        //Define some local helper functions to help with the loop below
        void AddPointToPointList <T>(List <T> list, T point) where T : MapPoint
        {
            if (!list.Contains(point))
            {
                list.Add(point);
            }
        }

        //Voronoi and Delaunay edges. Each edge point to two cells and two corners, so we can store both the sites and corners into a single edge object (thus making two edges into one object)
        foreach (var voronoiEdge in voronoi.Edges)
        {
            if (voronoiEdge.ClippedEnds == null)
            {
                continue;
            }

            CellEdge edge = new CellEdge();
            edge.index = edges.Count;

            //Set the voronoi edge
            edge.v0 = corners.First(x => x.position == voronoiEdge.ClippedEnds[LR.LEFT]);
            edge.v1 = corners.First(x => x.position == voronoiEdge.ClippedEnds[LR.RIGHT]);

            //Set the Delaunay edge
            edge.d0 = cells.First(x => x.position == voronoiEdge.LeftSite.Coord);
            edge.d1 = cells.First(x => x.position == voronoiEdge.RightSite.Coord);

            edges.Add(edge);

            /*Set the relationships*/

            //Set the relationship between this edge and the connected cells centers/corners
            edge.d0.borderEdges.Add(edge);
            edge.d1.borderEdges.Add(edge);
            edge.v0.connectedEdges.Add(edge);
            edge.v1.connectedEdges.Add(edge);

            //Set the relationship between the CELL CENTERS connected to this edge
            AddPointToPointList(edge.d0.neighborCells, edge.d1);
            AddPointToPointList(edge.d1.neighborCells, edge.d0);

            //Set the relationship between the CORNERS connected to this edge
            AddPointToPointList(edge.v0.neighborCorners, edge.v1);
            AddPointToPointList(edge.v1.neighborCorners, edge.v0);

            //Set the relationship of the CORNERS connected to this edge and the CELL CENTERS connected to this edge
            AddPointToPointList(edge.d0.cellCorners, edge.v0);
            AddPointToPointList(edge.d0.cellCorners, edge.v1);

            AddPointToPointList(edge.d1.cellCorners, edge.v0);
            AddPointToPointList(edge.d1.cellCorners, edge.v1);

            //Same as above, but the other way around
            AddPointToPointList(edge.v0.touchingCells, edge.d0);
            AddPointToPointList(edge.v0.touchingCells, edge.d1);

            AddPointToPointList(edge.v1.touchingCells, edge.d0);
            AddPointToPointList(edge.v1.touchingCells, edge.d1);
        }
    }