Implemented eroder.
This commit is contained in:
		
							
								
								
									
										234
									
								
								Game/Assets/Scripts/Generator/TerrainEroder.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								Game/Assets/Scripts/Generator/TerrainEroder.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,234 @@
 | 
			
		||||
using System;
 | 
			
		||||
using System.Collections.Generic;
 | 
			
		||||
using System.Linq;
 | 
			
		||||
using System.Text;
 | 
			
		||||
using TransportGame.Business;
 | 
			
		||||
using TransportGame.Model;
 | 
			
		||||
 | 
			
		||||
namespace TransportGame.Generator
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Simulates water erosion
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public class TerrainEroder
 | 
			
		||||
    {
 | 
			
		||||
        #region Constants
 | 
			
		||||
 | 
			
		||||
        /// <summary> Direction vector (X)</summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// In order: Center, North, North-East, East, South-East, South, South-West, West, North-West
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        readonly static private int[] DirectionsX = { 0, 0, 1, 1, 1, 0, -1, -1, -1 };
 | 
			
		||||
 | 
			
		||||
        /// <summary> Direction vector (Y)</summary>
 | 
			
		||||
        /// <remarks>
 | 
			
		||||
        /// In order: Center, North, North-East, East, South-East, South, South-West, West, North-West
 | 
			
		||||
        /// </remarks>
 | 
			
		||||
        readonly static private int[] DirectionsY = { 0, -1, -1, 0, 1, 1, 1, 0, -1 };
 | 
			
		||||
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        #region Public properties
 | 
			
		||||
        
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the terrain to erode
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public Map Terrain { get; private set; }
 | 
			
		||||
        
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        protected int SpringCount { get { return ConfigurationManager.TerrGenConfig.ErodePoints; } }
 | 
			
		||||
        protected int IterationCount { get { return ConfigurationManager.TerrGenConfig.ErodeIterations; } }
 | 
			
		||||
        protected float ErosionAmount { get { return ConfigurationManager.TerrGenConfig.ErodeAmountPercent; } }
 | 
			
		||||
 | 
			
		||||
        #region Private variables
 | 
			
		||||
 | 
			
		||||
        private Random random = new Random();
 | 
			
		||||
        private float[,] water;
 | 
			
		||||
        private int[] springsX;
 | 
			
		||||
        private int[] springsY;
 | 
			
		||||
 | 
			
		||||
        #endregion
 | 
			
		||||
 | 
			
		||||
        public TerrainEroder(Map terrain)
 | 
			
		||||
        {
 | 
			
		||||
            this.Terrain = terrain;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public void Erode()
 | 
			
		||||
        {
 | 
			
		||||
            Initialize();
 | 
			
		||||
            GenerateSprings();
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < IterationCount; i++)
 | 
			
		||||
                NextIteration();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void Initialize()
 | 
			
		||||
        {
 | 
			
		||||
            // Initialize matrices
 | 
			
		||||
            water = new float[Terrain.Width, Terrain.Height];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void GenerateSprings()
 | 
			
		||||
        {
 | 
			
		||||
            // Step 1: Generate random points, which will be the water springs
 | 
			
		||||
            springsX = new int[SpringCount];
 | 
			
		||||
            springsY = new int[SpringCount];
 | 
			
		||||
 | 
			
		||||
            for (int i = 0; i < SpringCount; i++)
 | 
			
		||||
            {
 | 
			
		||||
                springsX[i] = random.Next(0, Terrain.Width);
 | 
			
		||||
                springsY[i] = random.Next(0, Terrain.Height);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Step 2: Find local maximums, where the springs are placed
 | 
			
		||||
            bool changed;
 | 
			
		||||
            int iterations = 100;
 | 
			
		||||
 | 
			
		||||
            do
 | 
			
		||||
            {
 | 
			
		||||
                changed = false;
 | 
			
		||||
                for (int i = 0; i < SpringCount; i++)
 | 
			
		||||
                {
 | 
			
		||||
                    // Find best neighbour
 | 
			
		||||
                    float maxHeight = Terrain[springsX[i], springsY[i]];
 | 
			
		||||
                    int maxDir = 0;
 | 
			
		||||
 | 
			
		||||
                    ForEachDirection(springsX[i], springsY[i], (dx, dy, dir) =>
 | 
			
		||||
                        {
 | 
			
		||||
                            if (Terrain[dx, dy] >= maxHeight)
 | 
			
		||||
                            {
 | 
			
		||||
                                maxHeight = Terrain[dx, dy];
 | 
			
		||||
                                maxDir = dir;
 | 
			
		||||
                            }
 | 
			
		||||
                        });
 | 
			
		||||
                    
 | 
			
		||||
                    if (maxDir > 0)
 | 
			
		||||
                    {
 | 
			
		||||
                        springsX[i] += DirectionsX[maxDir];
 | 
			
		||||
                        springsY[i] += DirectionsY[maxDir];
 | 
			
		||||
                        changed = true;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                --iterations;
 | 
			
		||||
            } while (changed && iterations > 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void NextIteration()
 | 
			
		||||
        {
 | 
			
		||||
            Step1_RainAndErode();
 | 
			
		||||
            Step2_MoveWater();
 | 
			
		||||
            Step3_Evaporate();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void Step1_RainAndErode()
 | 
			
		||||
        {
 | 
			
		||||
            // Springs generate a lot of water
 | 
			
		||||
            for (int i = 0; i < SpringCount; i++)
 | 
			
		||||
                water[springsX[i], springsY[i]] += 100f;
 | 
			
		||||
 | 
			
		||||
            // Rain in the rest of the terrain
 | 
			
		||||
            for (int x = 0; x < Terrain.Width; x++)
 | 
			
		||||
                for (int y = 0; y < Terrain.Height; y++)
 | 
			
		||||
                {
 | 
			
		||||
                    water[x, y] += 1f;
 | 
			
		||||
 | 
			
		||||
                    // Erode some terrain
 | 
			
		||||
                    Terrain[x, y] -= ErosionAmount * water[x, y];
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void Step2_MoveWater()
 | 
			
		||||
        {
 | 
			
		||||
            for (int x = 0; x < Terrain.Width; x++)
 | 
			
		||||
                for (int y = 0; y < Terrain.Height; y++)
 | 
			
		||||
                {
 | 
			
		||||
                    List<int> directions = new List<int>();
 | 
			
		||||
 | 
			
		||||
                    // Find relevant directions
 | 
			
		||||
                    ForEachDirection(x, y, (dx, dy, dir) =>
 | 
			
		||||
                        {
 | 
			
		||||
                            if (Level(x, y) > Level(dx, dy))
 | 
			
		||||
                                directions.Add(dir);
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                    // Order directions by level
 | 
			
		||||
                    directions.Sort((dir1, dir2) =>
 | 
			
		||||
                        {
 | 
			
		||||
                            float level1 = Level(x + DirectionsX[dir1], y + DirectionsY[dir1]);
 | 
			
		||||
                            float level2 = Level(x + DirectionsX[dir2], y + DirectionsY[dir2]);
 | 
			
		||||
 | 
			
		||||
                            return Convert.ToInt32(100 * (level1 - level2));
 | 
			
		||||
                        });
 | 
			
		||||
 | 
			
		||||
                    // Distribute water
 | 
			
		||||
                    for (int dirIndex = 0; dirIndex < directions.Count; dirIndex++)
 | 
			
		||||
                    {
 | 
			
		||||
                        int dir = directions[dirIndex];
 | 
			
		||||
 | 
			
		||||
                        // We distribute all the water equally to cells from 
 | 
			
		||||
                        // directions 0..dir.
 | 
			
		||||
                        float distributeAmount = water[x, y] / (dirIndex + 1);
 | 
			
		||||
 | 
			
		||||
                        // If the resulting level is higher than the level of the next direction,
 | 
			
		||||
                        // we only add the level difference
 | 
			
		||||
                        if (dirIndex < directions.Count - 1)
 | 
			
		||||
                        {
 | 
			
		||||
                            int dx = x + DirectionsX[dir], dy = y + DirectionsY[dir];
 | 
			
		||||
                            int dx1 = x + DirectionsX[directions[dirIndex + 1]], dy1 = y + DirectionsY[directions[dirIndex + 1]];
 | 
			
		||||
 | 
			
		||||
                            distributeAmount = Math.Min(distributeAmount, Level(dx1, dy1) - Level(dx, dy));
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        // Distribute water & sediment
 | 
			
		||||
                        for (int dirDistribIndex = 0; dirDistribIndex < dirIndex; dirDistribIndex++)
 | 
			
		||||
                        {
 | 
			
		||||
                            int dirDistrib = directions[dirDistribIndex];
 | 
			
		||||
                            water[x + DirectionsX[dirDistrib], y + DirectionsY[dirDistrib]] += distributeAmount;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                        // Subtract from current cell
 | 
			
		||||
                        water[x, y] -= distributeAmount * (dirIndex + 1);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void Step3_Evaporate()
 | 
			
		||||
        {
 | 
			
		||||
            for (int x = 0; x < Terrain.Width; x++)
 | 
			
		||||
                for (int y = 0; y < Terrain.Height; y++)
 | 
			
		||||
                {
 | 
			
		||||
                    // Some sediment deposits
 | 
			
		||||
                    Terrain[x, y] += water[x, y] * ErosionAmount;
 | 
			
		||||
 | 
			
		||||
                    // Water evaporates
 | 
			
		||||
                    water[x, y] *= .5f;
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void ForEachDirection(int x, int y, Action<int, int, int> action)
 | 
			
		||||
        {
 | 
			
		||||
            for (int dir = 1; dir < DirectionsX.Length; dir++)
 | 
			
		||||
            {
 | 
			
		||||
                int dx = x + DirectionsX[dir];
 | 
			
		||||
                int dy = y + DirectionsY[dir];
 | 
			
		||||
 | 
			
		||||
                if (Terrain.IsInside(dx, dy))
 | 
			
		||||
                    action(dx, dy, dir);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the soil + water level
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="x">x coordinate</param>
 | 
			
		||||
        /// <param name="y">y coordinate</param>
 | 
			
		||||
        /// <returns>level</returns>
 | 
			
		||||
        private float Level(int x, int y)
 | 
			
		||||
        {
 | 
			
		||||
            return Terrain[x, y] + water[x, y];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -15,10 +15,13 @@ namespace TransportGame.Generator
 | 
			
		||||
{
 | 
			
		||||
    public class TerrainGenerator
 | 
			
		||||
    {
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the underlying noise generator
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        public NoiseGenerator Noise { get; set; }
 | 
			
		||||
 | 
			
		||||
        private System.Random random = new System.Random();
 | 
			
		||||
        
 | 
			
		||||
 | 
			
		||||
        public TerrainGenerator()
 | 
			
		||||
        {
 | 
			
		||||
            Noise = new PerlinNoiseGenerator();
 | 
			
		||||
@@ -48,10 +51,10 @@ namespace TransportGame.Generator
 | 
			
		||||
            map.WaterLevel = Mathf.Pow(waterAmount, ConfigurationManager.TerrGenConfig.WaterNonLinearPower) * map.Biome.Height;
 | 
			
		||||
 | 
			
		||||
            DumpData(map, "1generated.map");
 | 
			
		||||
            
 | 
			
		||||
 | 
			
		||||
            // Simulate water erosion
 | 
			
		||||
            //Erode(map);
 | 
			
		||||
            //DumpData(map, "2eroded.map");
 | 
			
		||||
            new TerrainEroder(map).Erode();
 | 
			
		||||
            DumpData(map, "2eroded.map");
 | 
			
		||||
 | 
			
		||||
            return map;
 | 
			
		||||
        }
 | 
			
		||||
@@ -68,16 +71,9 @@ namespace TransportGame.Generator
 | 
			
		||||
        {
 | 
			
		||||
            for (int x = 0; x < map.Width; ++x)
 | 
			
		||||
                for (int y = 0; y < map.Height; ++y)
 | 
			
		||||
                    map[x, y] = Noise.Generate(x, y, 0, 1);
 | 
			
		||||
                    map.Heights[x, y] = Noise.Generate(x, y, 0, 1);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        //private void Erode(Map map)
 | 
			
		||||
        //{
 | 
			
		||||
        //    float[,] water = new float[map.Width, map.Height];
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        //}
 | 
			
		||||
 | 
			
		||||
        private void DumpData(Map map, string filename)
 | 
			
		||||
        {
 | 
			
		||||
            XmlSerializer serializer = new XmlSerializer(typeof(Map));
 | 
			
		||||
 
 | 
			
		||||
@@ -20,5 +20,14 @@ namespace Assets.Scripts.Model.Config
 | 
			
		||||
 | 
			
		||||
        [XmlElement("waterNonLinearPower")]
 | 
			
		||||
        public float WaterNonLinearPower { get; set; }
 | 
			
		||||
 | 
			
		||||
        [XmlElement("erodePoints")]
 | 
			
		||||
        public int ErodePoints { get; set; }
 | 
			
		||||
 | 
			
		||||
        [XmlElement("erodeIterations")]
 | 
			
		||||
        public int ErodeIterations { get; set; }
 | 
			
		||||
 | 
			
		||||
        [XmlElement("erodeAmountPercent")]
 | 
			
		||||
        public float ErodeAmountPercent { get; set; }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@ namespace TransportGame.Model
 | 
			
		||||
        public Biome Biome { get; set; }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets the heights array
 | 
			
		||||
        /// Gets the heights array in range [0,1]
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        [XmlIgnore()]
 | 
			
		||||
        public float[,] Heights
 | 
			
		||||
@@ -102,7 +102,7 @@ namespace TransportGame.Model
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Gets or sets the cell at specified position
 | 
			
		||||
        /// Gets or sets the cell at specified position in range [0, Biome.Height]
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="x">X</param>
 | 
			
		||||
        /// <param name="y">Y</param>
 | 
			
		||||
@@ -112,11 +112,11 @@ namespace TransportGame.Model
 | 
			
		||||
        {
 | 
			
		||||
            get
 | 
			
		||||
            {
 | 
			
		||||
                return grid[x, y];
 | 
			
		||||
                return grid[x, y] * Biome.Height;
 | 
			
		||||
            }
 | 
			
		||||
            set
 | 
			
		||||
            {
 | 
			
		||||
                grid[x, y] = value;
 | 
			
		||||
                grid[x, y] = value / Biome.Height;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -140,7 +140,18 @@ namespace TransportGame.Model
 | 
			
		||||
        /// <returns></returns>
 | 
			
		||||
        public bool IsWater(int x, int y)
 | 
			
		||||
        {
 | 
			
		||||
            return grid[x, y] * Biome.Height <= WaterLevel;
 | 
			
		||||
            return this[x, y] <= WaterLevel;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /// <summary>
 | 
			
		||||
        /// Returns true if given coordinates is inside the map
 | 
			
		||||
        /// </summary>
 | 
			
		||||
        /// <param name="x">X</param>
 | 
			
		||||
        /// <param name="y">Y</param>
 | 
			
		||||
        /// <returns>True if coordinates are inside the map</returns>
 | 
			
		||||
        public bool IsInside(int x, int y)
 | 
			
		||||
        {
 | 
			
		||||
            return x >= 0 && y >= 0 && x < Width && y < Height;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user