private void GroundToSediment()
        {
            if (Configs.TerrainSolubility == 0)
            {
                return;
            }

            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    float waterVolume = WaterMap[x, y] - SoilMap[x, y];
                    if (waterVolume <= 0)
                    {
                        continue;
                    }

                    float amountToRemove = Configs.TerrainSolubility * waterVolume;
                    amountToRemove = Math.Min(amountToRemove, SoilMap[x, y] - RockMap[x, y]);
                    SoilMap[x, y] -= amountToRemove;

                    // TODO: Talvez a água pudesse converter a camada de rocha para sedimento caso não haja solo suficiente para atingir a saturação geral
                }
            }

            UpdateMeshes = true;
            UpdateShades = true;
        }
Ejemplo n.º 2
0
        public void CalculateHeat()
        {
            if (HeatMap == null || HeatMap.GetLength(0) != SoilMap.GetLength(0) || HeatMap.GetLength(1) != SoilMap.GetLength(1))
            {
                HeatMap = new float[SoilMap.GetLength(0), SoilMap.GetLength(1)];
            }

            switch (Type)
            {
            case HeatTypes.SoilDepth:
                CalculateSoilDepth();
                break;

            case HeatTypes.WaterDepth:
                CalculateWaterDepth();
                break;

            case HeatTypes.SoilHumidity:
                CalculateSoilHumidity();
                break;

            case HeatTypes.Inclination:
                CalculateInclination();
                break;
            }
        }
        private void DrainWater()
        {
            if (Configs.EvaporationFactor == 0)
            {
                return;
            }

            float evaporationPercent = (1 - Configs.EvaporationFactor);

            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    float waterVolume = WaterMap[x, y] - SoilMap[x, y];
                    if (waterVolume <= 0)
                    {
                        continue;
                    }

                    float diff = waterVolume - (waterVolume * evaporationPercent);
                    diff              *= SurfaceDrainModifiers[SurfaceMap[x, y]];
                    WaterMap[x, y]    -= diff;
                    SoilMap[x, y]     += Configs.TerrainSolubility * diff;
                    HumidityMap[x, y] += diff;
                    if (HumidityMap[x, y] > 1.0f)
                    {
                        HumidityMap[x, y] = 1.0f;
                    }
                }
            }

            UpdateMeshes = true;
            UpdateShades = true;
        }
        // As inclinações são avaliadas de cima para baixo
        private List<float> GetLocalInclines(int x, int y)
        {
            List<float> inclines = new List<float>();

            if (x != 0)
            {
                float incline = SoilMap[x, y] - SoilMap[x - 1, y];
                if (incline > 0)
                    inclines.Add(incline);
            }
            if (y != 0)
            {
                float incline = SoilMap[x, y] - SoilMap[x, y - 1];
                if (incline > 0)
                    inclines.Add(incline);
            }
            if (x != SoilMap.GetLength(0) - 1)
            {
                float incline = SoilMap[x, y] - SoilMap[x + 1, y];
                if (incline > 0)
                    inclines.Add(incline);
            }
            if (y != SoilMap.GetLength(1) - 1)
            {
                float incline = SoilMap[x, y] - SoilMap[x, y + 1];
                if (incline > 0)
                    inclines.Add(incline);
            }

            return inclines;
        }
        private void SetSoilType(Vector3 point)
        {
            int maxX = SoilMap.GetLength(0);
            int maxZ = SoilMap.GetLength(1);

            int begX = (int)((point.x / soilLayer.terrainData.size.x) * maxX);
            int begZ = (int)((point.z / soilLayer.terrainData.size.z) * maxZ);

            begX -= EditConfigs.BrushSize;
            begZ -= EditConfigs.BrushSize;

            int end = EditConfigs.BrushSize * 2;

            int type = (int)EditConfigs.SurfacePaintMode;

            for (int x = 0; x < end; ++x)
            {
                int curX = begX + x;
                if (curX < 0 || curX >= maxX)
                {
                    continue;
                }

                for (int z = 0; z < end; ++z)
                {
                    int curZ = begZ + z;
                    if (curZ < 0 || curZ >= maxZ)
                    {
                        continue;
                    }

                    SurfaceMap[curZ, curX] = type;
                }
            }
        }
Ejemplo n.º 6
0
        private void TransformVonNeumann()
        {
            // Transformação usando vizinhança Von Neumann

            int topX = SoilMap.GetLength(0);
            int topY = SoilMap.GetLength(1);

            float[,] baseHeights = SoilMap.Clone() as float[, ];

            // Loop geral do mapa
            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    // Fazer a média da altura com base nos vizinhos

                    float sumHeights   = 0.0f;
                    int   countHeights = 0;

                    // Primeiro somar os vizinhos na horizontal
                    for (int relX = -Configs.Range; relX <= Configs.Range; relX++)
                    {
                        int absX = x + relX;
                        if (absX < 0 || absX >= topX)
                        {
                            continue;
                        }

                        sumHeights += baseHeights[absX, y];
                        countHeights++;
                    }

                    // Depois na vertical
                    for (int relY = -Configs.Range; relY <= Configs.Range; relY++)
                    {
                        int absY = y + relY;
                        if (absY < 0 || absY >= topY)
                        {
                            continue;
                        }

                        sumHeights += baseHeights[x, absY];
                        countHeights++;
                    }

                    // Subtrair o valor da célula central que foi somado duas vezes
                    sumHeights -= baseHeights[x, y];
                    countHeights--;

                    // Aplicar a média dos valores
                    if (countHeights > 0)
                    {
                        float diff = (sumHeights / countHeights) - SoilMap[x, y];
                        SoilMap[x, y] += diff * Configs.Factor;
                    }
                }
            }
        }
        private void LowerTerrain(Vector3 point)
        {
            // Método para referência https://forum.unity3d.com/threads/edit-terrain-in-real-time.98410/

            int   terX = (int)((point.x / soilLayer.terrainData.size.x) * SoilMap.GetLength(0));
            int   terZ = (int)((point.z / soilLayer.terrainData.size.z) * SoilMap.GetLength(1));
            float y    = SoilMap[terX, terZ];

            y -= 0.001f;
            float[,] height     = new float[1, 1];
            height[0, 0]        = y;
            SoilMap[terX, terZ] = y;
            soilLayer.terrainData.SetHeights(terX, terZ, height);
        }
Ejemplo n.º 8
0
        private void TransformMoore()
        {
            // Transformação usando vizinhança Moore

            int topX = SoilMap.GetLength(0);
            int topY = SoilMap.GetLength(1);

            float[,] baseHeights = SoilMap.Clone() as float[, ];

            // Loop geral do mapa
            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    // Fazer a média da altura com base nos vizinhos
                    float sumHeights   = 0.0f;
                    int   countHeights = 0;

                    // Loop interno dos vizinhos
                    for (int relX = -Configs.Range; relX <= Configs.Range; relX++)
                    {
                        int absX = x + relX;
                        if (absX < 0 || absX >= topX)
                        {
                            continue;
                        }

                        for (int relY = -Configs.Range; relY <= Configs.Range; relY++)
                        {
                            int absY = y + relY;
                            if (absY < 0 || absY >= topY)
                            {
                                continue;
                            }

                            sumHeights += baseHeights[absX, absY];
                            countHeights++;
                        }
                    }

                    // Aplicar a média dos valores
                    if (countHeights > 0)
                    {
                        float diff = (sumHeights / countHeights) - SoilMap[x, y];
                        SoilMap[x, y] += diff * Configs.Factor;
                    }
                }
            }
        }
Ejemplo n.º 9
0
        private void CalculateWaterDepth()
        {
            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    float value = (WaterMap[x, y] - SoilMap[x, y]) * 10;
                    if (value < 0)
                    {
                        value = 0;
                    }
                    else if (value > 1.0f)
                    {
                        value = 1.0f;
                    }

                    HeatMap[x, y] = value;
                }
            }
        }
Ejemplo n.º 10
0
        private void CalculateInclination()
        {
            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    float value = GetHighestIncline(x, y) * 50;
                    if (value < 0)
                    {
                        value = 0;
                    }
                    else if (value > 1.0f)
                    {
                        value = 1.0f;
                    }

                    HeatMap[x, y] = value;
                }
            }
        }
Ejemplo n.º 11
0
        private void CalculateSoilHumidity()
        {
            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    float value = HumidityMap[x, y] * 10;
                    if (value < 0)
                    {
                        value = 0;
                    }
                    else if (value > 1.0f)
                    {
                        value = 1.0f;
                    }

                    HeatMap[x, y] = value;
                }
            }
        }
        public void LoadMaps()
        {
            rockLayer.terrainData.heightmapResolution = RockMap.GetLength(0);
            rockLayer.terrainData.SetHeights(0, 0, RockMap);

            soilLayer.terrainData.heightmapResolution = SoilMap.GetLength(0);
            soilLayer.terrainData.alphamapResolution  = SoilMap.GetLength(0);
            soilLayer.terrainData.SetHeights(0, 0, SoilMap);

            waterLayer.terrainData.heightmapResolution = SoilMap.GetLength(0);
            waterLayer.terrainData.alphamapResolution  = SoilMap.GetLength(0);
            waterLayer.terrainData.SetHeights(0, 0, WaterMap);

            heatLayer.terrainData.heightmapResolution = SoilMap.GetLength(0);
            heatLayer.terrainData.alphamapResolution  = SoilMap.GetLength(0);
            heatLayer.terrainData.SetHeights(0, 0, SoilMap);

            ResetAllTransforms();
            UpdateView(true);
        }
Ejemplo n.º 13
0
        private float GetHighestIncline(int x, int y)
        {
            float highest = 0;

            if (x != 0)
            {
                float incline = SoilMap[x, y] - SoilMap[x - 1, y];
                if (incline > highest)
                {
                    highest = incline;
                }
            }
            if (y != 0)
            {
                float incline = SoilMap[x, y] - SoilMap[x, y - 1];
                if (incline > highest)
                {
                    highest = incline;
                }
            }
            if (x != SoilMap.GetLength(0) - 1)
            {
                float incline = SoilMap[x, y] - SoilMap[x + 1, y];
                if (incline > highest)
                {
                    highest = incline;
                }
            }
            if (y != SoilMap.GetLength(1) - 1)
            {
                float incline = SoilMap[x, y] - SoilMap[x, y + 1];
                if (incline > highest)
                {
                    highest = incline;
                }
            }

            return(highest);
        }
Ejemplo n.º 14
0
        public void ApplyMoore()
        {
            int topX = SoilMap.GetLength(0);
            int topY = SoilMap.GetLength(1);

            float[,] baseHeights = SoilMap.Clone() as float[, ];

            int startX = GetStartingX();
            int startY = GetStartingY();
            int endX   = GetEndingX();
            int endY   = GetEndingY();

            int incStartY = GetStartingYIncrement();
            int incEndY   = GetEndingYIncrement();

            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    // Fazer a média da altura com base nas alturas vizinhas no hemisfério Sul
                    // Apenas considerar alterações para baixo

                    float sumHeights   = 0.0f;
                    int   countHeights = 0;

                    int localStartY = startY;
                    int localEndY   = endY;

                    for (int relX = startX; relX <= endX; relX++)
                    {
                        int absX = x + relX;
                        if (absX < 0 || absX >= topX)
                        {
                            continue;
                        }

                        for (int relY = localStartY; relY <= localEndY; relY++)
                        {
                            int absY = y + relY;
                            if (absY < 0 || absY >= topY)
                            {
                                continue;
                            }

                            sumHeights += baseHeights[absX, absY];
                            countHeights++;
                        }

                        localStartY += incStartY;
                        localEndY   += incEndY;
                    }

                    if (countHeights > 0)
                    {
                        float avg = sumHeights / countHeights;
                        if (avg < SoilMap[x, y])
                        {
                            float diff = avg - SoilMap[x, y];
                            SoilMap[x, y] += diff * Configs.Factor;
                        }
                    }
                }
            }
        }
        public void CollectStats()
        {
            float soilMass = 0;
            float waterMass = 0;
            float humidityMass = 0;
            float highSoil = 0;
            float highWater = 0;
            float highHumidity = 0;
            float highHeight = 0;
            float highIncline = 0;
            float lowSoil = 0;
            float lowWater = 0;
            float lowHumidity = 0;
            float lowHeight = 0;
            float lowIncline = 1;
            float avgSoil = 0;
            float avgWater = 0;
            float avgHumidity = 0;
            float avgHeight = 0;
            float avgIncline = 0;

            float totalHeight = 0;

            float totalIncline = 0;
            int inclineCount = 0;

            int[] surfaceCounter = new int[(int)SurfaceType.Count];

            int topX = SoilMap.GetLength(0);
            int topY = SoilMap.GetLength(1);
            int area = topX * topY;

            for (int x = 0; x < topX; x++)
            {
                for (int y = 0; y < topY; y++)
                {
                    float localSoil = SoilMap[x, y] - RockMap[x, y];
                    float localWater = WaterMap[x, y] - SoilMap[x, y];
                    float localHumidity = HumidityMap[x, y];
                    float localHeight = SoilMap[x, y];

                    if (localSoil < 0) localSoil = 0;
                    if (localWater < 0) localWater = 0;

                    List<float> localInclines = GetLocalInclines(x, y);

                    soilMass        += localSoil;
                    waterMass       += localWater;
                    humidityMass    += localHumidity;
                    totalHeight     += localHeight;

                    if (localSoil       > highSoil      ) highSoil      = localSoil;
                    if (localWater      > highWater     ) highWater     = localWater;
                    if (localHumidity   > highHumidity  ) highHumidity  = localHumidity;
                    if (localHeight     > highHeight    ) highHeight    = localHeight;

                    if (localSoil       < lowSoil       || lowSoil      == 0) lowSoil       = localSoil;
                    if (localWater      < lowWater      || lowWater     == 0) lowWater      = localWater;
                    if (localHumidity   < lowHumidity   || lowHumidity  == 0) lowHumidity   = localHumidity;
                    if (localHeight     < lowHeight     || lowHeight    == 0) lowHeight     = localHeight;

                    foreach (float incline in localInclines)
                    {
                        totalIncline += incline;
                        inclineCount++;

                        if (incline > highIncline) highIncline = incline;
                        if (incline < lowIncline) lowIncline = incline;
                    }

                    surfaceCounter[SurfaceMap[x, y]]++;
                }
            }

            avgSoil     = soilMass      / area;
            avgWater    = waterMass     / area;
            avgHumidity = humidityMass  / area;
            avgHeight   = totalHeight   / area;
            avgIncline  = totalIncline  / inclineCount;

            Output[(int)Stats.SoilMass      ] = soilMass    .ToString();
            Output[(int)Stats.WaterMass     ] = waterMass   .ToString();
            Output[(int)Stats.HumidityMass  ] = humidityMass.ToString();
            Output[(int)Stats.HighSoil      ] = highSoil    .ToString();
            Output[(int)Stats.HighWater     ] = highWater   .ToString();
            Output[(int)Stats.HighHumidity  ] = highHumidity.ToString();
            Output[(int)Stats.HighHeight    ] = highHeight  .ToString();
            Output[(int)Stats.HighIncline   ] = highIncline .ToString();
            Output[(int)Stats.LowSoil       ] = lowSoil     .ToString();
            Output[(int)Stats.LowWater      ] = lowWater    .ToString();
            Output[(int)Stats.LowHumidity   ] = lowHumidity .ToString();
            Output[(int)Stats.LowHeight     ] = lowHeight   .ToString();
            Output[(int)Stats.LowIncline    ] = lowIncline  .ToString();
            Output[(int)Stats.AvgSoil       ] = avgSoil     .ToString();
            Output[(int)Stats.AvgWater      ] = avgWater    .ToString();
            Output[(int)Stats.AvgHumidity   ] = avgHumidity .ToString();
            Output[(int)Stats.AvgHeight     ] = avgHeight   .ToString();
            Output[(int)Stats.AvgIncline    ] = avgIncline  .ToString();
            Output[(int)Stats.MostSurface   ] = GetMostSurface(surfaceCounter);
        }
        private void DoWaterFlow()
        {
            int soilType = (int)SurfaceType.Soil;

            // Loop geral do mapa
            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    // Altura do terreno + altura da água
                    float localSurfaceHeight = WaterMap[x, y];
                    float localWaterVolume   = localSurfaceHeight - SoilMap[x, y];

                    if (localWaterVolume <= 0)
                    {
                        continue;
                    }

                    // Média das alturas
                    float avgSurfaceHeight = 0;
                    int   countHeights     = 0;

                    // Soma das diferenças de altura positivas
                    float totalDifference = 0;

                    // Loop horizontal
                    VonNeumannTransform(x, y, SoilMap,
                                        (ref float localHeight, ref float nearbyHeight, int nearbyX, int nearbyY) =>
                    {
                        float nearbySurfaceHeight = WaterMap[nearbyX, nearbyY];
                        float difference          = localSurfaceHeight - nearbySurfaceHeight;

                        if (difference < 0)
                        {
                            return;
                        }

                        totalDifference  += difference;
                        avgSurfaceHeight += nearbySurfaceHeight;
                        countHeights++;
                    }
                                        );

                    // Se não houver diferenças positivas prosseguir
                    if (totalDifference == 0)
                    {
                        continue;
                    }

                    avgSurfaceHeight /= countHeights;

                    float deltaSurfaceHeight = localSurfaceHeight - avgSurfaceHeight;

                    float totalDeltaWater = 0;

                    // Loop horizontal
                    VonNeumannTransform(x, y, SoilMap,
                                        (ref float localHeight, ref float nearbyHeight, int nearbyX, int nearbyY) =>
                    {
                        float nearbySurfaceHeight = WaterMap[nearbyX, nearbyY];

                        if (nearbySurfaceHeight >= localSurfaceHeight)
                        {
                            return;
                        }

                        float difference = localSurfaceHeight - nearbySurfaceHeight;
                        float deltaWater = Math.Min(localWaterVolume, deltaSurfaceHeight) * (difference / totalDifference);

                        WaterMap[nearbyX, nearbyY] += deltaWater;
                        totalDeltaWater            += deltaWater;

                        // Quando o nível da água passar de um certo ponto, destruir a superfície local
                        if (WaterMap[nearbyX, nearbyY] - SoilMap[nearbyX, nearbyY] > 0.25f)
                        {
                            SurfaceMap[nearbyX, nearbyY] = soilType;
                        }
                    }
                                        );

                    if (totalDeltaWater > 0)
                    {
                        WaterMap[x, y] -= totalDeltaWater;
                        if (WaterMap[x, y] < SoilMap[x, y])
                        {
                            WaterMap[x, y] = SoilMap[x, y];
                        }

                        UpdateMeshes = true;
                        UpdateShades = true;
                    }
                }
            }
        }
Ejemplo n.º 17
0
        public void ApplyVonNeumann()
        {
            Directions direction = Configs.WindDirection;
            int        topX      = SoilMap.GetLength(0);
            int        topY      = SoilMap.GetLength(1);

            float[,] baseHeights = SoilMap.Clone() as float[, ];

            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    float sumHeights   = 0.0f;
                    int   countHeights = 0;

                    // Direções horizontais
                    if (direction == Directions.East || direction == Directions.West)
                    {
                        int from = direction == Directions.East ? -Configs.Range : 0;
                        int to   = direction == Directions.East ? 0 : Configs.Range;

                        // Todos os vizinhos verticais
                        for (int relX = -Configs.Range; relX <= Configs.Range; relX++)
                        {
                            int absX = x + relX;
                            if (absX < 0 || absX >= topX)
                            {
                                continue;
                            }

                            sumHeights += baseHeights[absX, y];
                            countHeights++;
                        }

                        // Metade dos vizinhos horizontais
                        for (int relY = from; relY <= to; relY++)
                        {
                            int absY = y + relY;
                            if (absY < 0 || absY >= topY)
                            {
                                continue;
                            }

                            sumHeights += baseHeights[x, absY];
                            countHeights++;
                        }

                        // Subtrair o valor da célula central que foi somado duas vezes
                        sumHeights -= baseHeights[x, y];
                        countHeights--;
                    }
                    // Direções verticais
                    if (direction == Directions.North || direction == Directions.South)
                    {
                        int from = direction == Directions.North ? -Configs.Range : 0;
                        int to   = direction == Directions.North ? 0 : Configs.Range;

                        // Metade dos vizinhos verticais
                        for (int relX = from; relX <= to; relX++)
                        {
                            int absX = x + relX;
                            if (absX < 0 || absX >= topX)
                            {
                                continue;
                            }

                            sumHeights += baseHeights[absX, y];
                            countHeights++;
                        }

                        // Todos os vizinhos horizontais
                        for (int relY = -Configs.Range; relY <= Configs.Range; relY++)
                        {
                            int absY = y + relY;
                            if (absY < 0 || absY >= topY)
                            {
                                continue;
                            }

                            sumHeights += baseHeights[x, absY];
                            countHeights++;
                        }

                        // Subtrair o valor da célula central que foi somado duas vezes
                        sumHeights -= baseHeights[x, y];
                        countHeights--;
                    }
                    // Direções diagonais
                    else
                    {
                        int fromX = 0;
                        int fromY = 0;
                        int toX   = 0;
                        int toY   = 0;

                        // Selecionar valores para representar a diagonal selecionada
                        switch (direction)
                        {
                        case Directions.Northeast:
                            fromX = -Configs.Range;
                            fromY = -Configs.Range;
                            break;

                        case Directions.Southeast:
                            toX   = Configs.Range;
                            fromY = -Configs.Range;
                            break;

                        case Directions.Southwest:
                            toX = Configs.Range;
                            toY = Configs.Range;
                            break;

                        case Directions.Northwest:
                            fromX = -Configs.Range;
                            toY   = Configs.Range;
                            break;
                        }

                        // Vizinhos verticais
                        for (int relX = fromX; relX <= toX; relX++)
                        {
                            int absX = x + relX;
                            if (absX < 0 || absX >= topX)
                            {
                                continue;
                            }

                            sumHeights += baseHeights[absX, y];
                            countHeights++;
                        }

                        // Vizinhos horizontais
                        for (int relY = fromY; relY <= toY; relY++)
                        {
                            int absY = y + relY;
                            if (absY < 0 || absY >= topY)
                            {
                                continue;
                            }

                            sumHeights += baseHeights[x, absY];
                            countHeights++;
                        }

                        // Subtrair o valor da célula central que foi somado duas vezes
                        sumHeights -= baseHeights[x, y];
                        countHeights--;
                    }

                    // Aplicar média
                    if (countHeights > 0)
                    {
                        float avg = sumHeights / countHeights;
                        if (avg < SoilMap[x, y])
                        {
                            float diff = avg - SoilMap[x, y];
                            SoilMap[x, y] += diff * Configs.Factor;
                        }
                    }
                }
            }
        }
        public override void ApplyTransform()
        {
            int topX = SoilMap.GetLength(0);
            int topY = SoilMap.GetLength(1);

            float[,] baseHeights = SoilMap.Clone() as float[, ];
            float[,] heightDiff  = new float[topX, topY];

            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    // Fazer a média da altura com base nas alturas vizinhas
                    // Apenas considerar variação para baixo

                    float sumHeights   = 0.0f;
                    int   countHeights = 0;

                    for (int relX = -1; relX <= 1; relX++)
                    {
                        int absX = x + relX;
                        if (absX < 0 || absX >= topX)
                        {
                            break;
                        }

                        for (int relY = -1; relY <= 1; relY++)
                        {
                            int absY = y + relY;
                            if (absY < 0 || absY >= topY)
                            {
                                break;
                            }

                            sumHeights += baseHeights[absX, absY];
                            countHeights++;
                        }
                    }

                    if (countHeights > 0)
                    {
                        float avg = sumHeights / countHeights;
                        if (avg < SoilMap[x, y])
                        {
                            SoilMap[x, y]    = avg;
                            heightDiff[x, y] = baseHeights[x, y] - avg;
                        }
                    }
                }
            }

            // Distribuir a massa de terra removida para os terrenos mais baixos
            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    int countLowLands = 0;

                    // Quantidade de nodos mais baixos
                    for (int relX = -1; relX <= 1; relX++)
                    {
                        int absX = x + relX;
                        if (absX < 0 || absX >= topX)
                        {
                            break;
                        }

                        for (int relY = -1; relY <= 1; relY++)
                        {
                            int absY = y + relY;
                            if (absY < 0 || absY >= topY)
                            {
                                break;
                            }

                            if (absX != x && absY != y && SoilMap[absX, absY] <= SoilMap[x, y])
                            {
                                countLowLands++;
                            }
                        }
                    }

                    // Distribuição
                    if (countLowLands > 0)
                    {
                        float depositPerPlot = heightDiff[x, y] / countLowLands;

                        for (int relX = -1; relX <= 1; relX++)
                        {
                            int absX = x + relX;
                            if (absX < 0 || absX >= topX)
                            {
                                break;
                            }

                            for (int relY = -1; relY <= 1; relY++)
                            {
                                int absY = y + relY;
                                if (absY < 0 || absY >= topY)
                                {
                                    break;
                                }

                                if (absX != x && absY != y && SoilMap[absX, absY] <= SoilMap[x, y])
                                {
                                    SoilMap[absX, absY] += depositPerPlot;
                                }
                            }
                        }
                    }
                    else
                    {
                        SoilMap[x, y] += heightDiff[x, y];
                    }
                }
            }
        }
        public void DoTransform()
        {
            // Loop geral do mapa
            for (int x = 0; x < SoilMap.GetLength(0); x++)
            {
                for (int y = 0; y < SoilMap.GetLength(1); y++)
                {
                    // Concreto não pode sofrer erosão, mas pode ser destruído por queda de materiais.
                    if (SurfaceMap[x, y] == (int)SurfaceType.Concrete)
                    {
                        continue;
                    }

                    float localSoilMass = SoilMap[x, y] - RockMap[x, y];

                    // Obter a maior inclinação, a soma das inclinações superiores à configuração e a quantidade esperada de material movido
                    float maxInclination   = 0.0f;
                    float sumInclinations  = 0.0f;
                    float sumMovedMaterial = 0.0f;

                    float thresholdInclination = Configs.MaxInclination * SurfaceInclinationModifiers[SurfaceMap[x, y]];
                    // Inclinação máxima reduzida até a metade de acordo com a umidade do local
                    thresholdInclination -= (HumidityMap[x, y] * thresholdInclination) / 2;

                    VonNeumannTransform(x, y, SoilMap,
                                        (ref float localHeight, ref float nearbyHeight, int nearbyX, int nearbyY) =>
                    {
                        float inclination = localHeight - SoilMap[nearbyX, nearbyY];
                        if (inclination > maxInclination)
                        {
                            maxInclination = inclination;
                        }
                        if (inclination > thresholdInclination)
                        {
                            sumInclinations  += inclination;
                            sumMovedMaterial += Configs.DistributionFactor * (inclination - thresholdInclination);
                        }
                    }
                                        );

                    // Se não houver nada para alterar prosseguir imediatamente
                    if (sumInclinations == 0.0)
                    {
                        continue;
                    }

                    // Limitar a quantidade de material movimentada se não houver solo o bastante
                    float movementLimitingFactor = 1.0f;
                    if (sumMovedMaterial > localSoilMass)
                    {
                        movementLimitingFactor = localSoilMass / sumMovedMaterial;
                    }

                    // Mover material para os vizinhos com inclinações superiores à configuração
                    sumMovedMaterial = 0.0f;

                    // Constante para os próximos cálculos
                    float inclinationDifference = (maxInclination - thresholdInclination);

                    int soilIndex = (int)SurfaceType.Soil;

                    VonNeumannTransform(x, y, SoilMap,
                                        (ref float localHeight, ref float nearbyHeight, int nearbyX, int nearbyY) =>
                    {
                        float inclination = localHeight - SoilMap[nearbyX, nearbyY];
                        if (inclination > thresholdInclination)
                        {
                            float movedMaterial         = Configs.DistributionFactor * inclinationDifference * (inclination / sumInclinations);
                            movedMaterial              *= movementLimitingFactor;
                            SoilMap[nearbyX, nearbyY]  += movedMaterial;
                            WaterMap[nearbyX, nearbyY] += movedMaterial;
                            sumMovedMaterial           += movedMaterial;

                            if (SurfaceMap[nearbyX, nearbyY] != soilIndex)
                            {
                                // Locais que recebem queda de material têm sua superfície destruída
                                SurfaceMap[nearbyX, nearbyY] = soilIndex;
                                UpdateTextures = true;
                            }
                        }
                    }
                                        );

                    if (sumMovedMaterial > 0)
                    {
                        SoilMap[x, y]  -= sumMovedMaterial;
                        WaterMap[x, y] -= sumMovedMaterial;
                        UpdateMeshes    = true;
                        UpdateShades    = true;

                        if (SurfaceMap[x, y] != soilIndex)
                        {
                            SurfaceMap[x, y] = soilIndex;
                            UpdateTextures   = true;
                        }
                    }
                }
            }
        }