Ejemplo n.º 1
0
    // Manages direction and actions of droplet such as direction, lifetime and sediment
    private void RunDropletCycle(Droplet newDrop)
    {
        for (int lifetime = 0; lifetime < dropletLifetime; lifetime++)
        {
            int   nodeX        = (int)newDrop.posX;
            int   nodeY        = (int)newDrop.posY;
            int   dropletIndex = nodeY * currentMapSize + nodeX;
            float cellOffsetX  = newDrop.posX - nodeX;
            float cellOffsetY  = newDrop.posY - nodeY;

            // Gets Droplet's height and direction
            HeightAndGradient heightAndGradient = CalculateHeightAndGradient(newDrop.posX, newDrop.posY);

            // Update droplet
            newDrop.directionX = (newDrop.directionX * inertia - heightAndGradient.gradientX * (1 - inertia));
            newDrop.directionY = (newDrop.directionY * inertia - heightAndGradient.gradientY * (1 - inertia));

            float len = Mathf.Sqrt(newDrop.directionX * newDrop.directionX + newDrop.directionY * newDrop.directionY);
            if (len != 0)
            {
                newDrop.directionX /= len;
                newDrop.directionY /= len;
            }
            newDrop.posX += newDrop.directionX;
            newDrop.posY += newDrop.directionY;

            // Check if droplet is on map
            if ((newDrop.directionX == 0 && newDrop.directionY == 0) || newDrop.posX < 0 || newDrop.posX >= currentMapSize - 1 || newDrop.posY < 0 || newDrop.posY >= currentMapSize - 1)
            {
                break;
            }
            CalculateSediment(newDrop, heightAndGradient, dropletIndex, cellOffsetX, cellOffsetY);
        }
    }
Ejemplo n.º 2
0
    // Calculate sediment capacity based on amount of water and height
    private void CalculateSediment(Droplet newDrop, HeightAndGradient heightAndGradient, int dropletIndex, float cellOffsetX, float cellOffsetY)
    {
        float newHeight        = CalculateHeightAndGradient(newDrop.posX, newDrop.posY).height;
        float deltaHeight      = newHeight - heightAndGradient.height;
        float sedimentCapacity = Mathf.Max(-deltaHeight * newDrop.newSpeed * newDrop.currentWater * sedimentMultiplier, minSediment); //Combined with minSediment to stop terrain spikes

        if (newDrop.sediment > sedimentCapacity || deltaHeight > 0)
        {
            DropSediment(deltaHeight, sedimentCapacity, dropletIndex, cellOffsetX, cellOffsetY, newDrop);
        }
        else
        {
            float amountToErode = Mathf.Min((sedimentCapacity - newDrop.sediment) * erosionSpeed, -deltaHeight);
            for (int brushPointIndex = 0; brushPointIndex < erosionBrushIndices[dropletIndex].Length; brushPointIndex++)
            {
                ErodeArea(dropletIndex, brushPointIndex, amountToErode, newDrop);
            }
        }
        newDrop.newSpeed      = Mathf.Sqrt(newDrop.newSpeed * newDrop.newSpeed + deltaHeight * gravity);
        newDrop.currentWater *= (1 - evaporateSpeed);
    }
Ejemplo n.º 3
0
    public void Erode(float[] map, int mapSize, int numIterations = 1, bool resetSeed = false)
    {
        Initialize(mapSize, resetSeed);

        for (int iteration = 0; iteration < numIterations; iteration++)
        {
            // Create water droplet at random point on map
            float posX     = prng.Next(0, mapSize - 1);
            float posY     = prng.Next(0, mapSize - 1);
            float dirX     = 0;
            float dirY     = 0;
            float speed    = initialSpeed;
            float water    = initialWaterVolume;
            float sediment = 0;

            for (int lifetime = 0; lifetime < maxDropletLifetime; lifetime++)
            {
                int nodeX        = (int)posX;
                int nodeY        = (int)posY;
                int dropletIndex = nodeY * mapSize + nodeX;
                // Calculate droplet's offset inside the cell (0,0) = at NW node, (1,1) = at SE node
                float cellOffsetX = posX - nodeX;
                float cellOffsetY = posY - nodeY;

                // Calculate droplet's height and direction of flow with bilinear interpolation of surrounding heights
                HeightAndGradient heightAndGradient = CalculateHeightAndGradient(map, mapSize, posX, posY);

                // Update the droplet's direction and position (move position 1 unit regardless of speed)
                dirX = (dirX * inertia - heightAndGradient.gradientX * (1 - inertia));
                dirY = (dirY * inertia - heightAndGradient.gradientY * (1 - inertia));
                // Normalize direction
                float len = Mathf.Sqrt(dirX * dirX + dirY * dirY);
                if (len != 0)
                {
                    dirX /= len;
                    dirY /= len;
                }
                posX += dirX;
                posY += dirY;

                // Stop simulating droplet if it's not moving or has flowed over edge of map
                if ((dirX == 0 && dirY == 0) || posX < 0 || posX >= mapSize - 1 || posY < 0 || posY >= mapSize - 1)
                {
                    break;
                }

                // Find the droplet's new height and calculate the deltaHeight
                float newHeight   = CalculateHeightAndGradient(map, mapSize, posX, posY).height;
                float deltaHeight = newHeight - heightAndGradient.height;

                // Calculate the droplet's sediment capacity (higher when moving fast down a slope and contains lots of water)
                float sedimentCapacity = Mathf.Max(-deltaHeight * speed * water * sedimentCapacityFactor, minSedimentCapacity);

                // If carrying more sediment than capacity, or if flowing uphill:
                if (sediment > sedimentCapacity || deltaHeight > 0)
                {
                    // If moving uphill (deltaHeight > 0) try fill up to the current height, otherwise deposit a fraction of the excess sediment
                    float amountToDeposit = (deltaHeight > 0) ? Mathf.Min(deltaHeight, sediment) : (sediment - sedimentCapacity) * depositSpeed;
                    sediment -= amountToDeposit;

                    // Add the sediment to the four nodes of the current cell using bilinear interpolation
                    // Deposition is not distributed over a radius (like erosion) so that it can fill small pits
                    map[dropletIndex]               += amountToDeposit * (1 - cellOffsetX) * (1 - cellOffsetY);
                    map[dropletIndex + 1]           += amountToDeposit * cellOffsetX * (1 - cellOffsetY);
                    map[dropletIndex + mapSize]     += amountToDeposit * (1 - cellOffsetX) * cellOffsetY;
                    map[dropletIndex + mapSize + 1] += amountToDeposit * cellOffsetX * cellOffsetY;
                }
                else
                {
                    // Erode a fraction of the droplet's current carry capacity.
                    // Clamp the erosion to the change in height so that it doesn't dig a hole in the terrain behind the droplet
                    float amountToErode = Mathf.Min((sedimentCapacity - sediment) * erodeSpeed, -deltaHeight);

                    // Use erosion brush to erode from all nodes inside the droplet's erosion radius
                    for (int brushPointIndex = 0; brushPointIndex < erosionBrushIndices[dropletIndex].Length; brushPointIndex++)
                    {
                        int   nodeIndex          = erosionBrushIndices[dropletIndex][brushPointIndex];
                        float weighedErodeAmount = amountToErode * erosionBrushWeights[dropletIndex][brushPointIndex];
                        float deltaSediment      = (map[nodeIndex] < weighedErodeAmount) ? map[nodeIndex] : weighedErodeAmount;
                        map[nodeIndex] -= deltaSediment;
                        sediment       += deltaSediment;
                    }
                }

                // Update droplet's speed and water content
                speed  = Mathf.Sqrt(speed * speed + deltaHeight * gravity);
                water *= (1 - evaporateSpeed);
            }
        }
    }
Ejemplo n.º 4
0
    public float[] Erode(float[] map, int mapSize, int numIterations = 1, bool resetSeed = false)
    {
        Initialize(mapSize, resetSeed);

        for (int iteration = 0; iteration < numIterations; iteration++)
        {
            float posX     = prng.Next(0, mapSize - 1);
            float posY     = prng.Next(0, mapSize - 1);
            float dirX     = 0;
            float dirY     = 0;
            float speed    = initialSpeed;
            float water    = initialWaterVolume;
            float sediment = 0;

            for (int lifetime = 0; lifetime < maxDropletLifetime; lifetime++)
            {
                int nodeX = (int)posX;
                int nodeY = (int)posY;

                int dropletIndex = nodeY * mapSize + nodeX;

                float cellOffsetX = posX - nodeX;
                float cellOffsetY = posY - nodeY;

                HeightAndGradient heightAndGradient = CalculateHeightAndGradient(map, mapSize, posX, posY);

                dirX = (dirX * inertia - heightAndGradient.gradientX * (1 - inertia));
                dirY = (dirY * inertia - heightAndGradient.gradientY * (1 - inertia));

                float len = Mathf.Sqrt(dirX * dirX + dirY * dirY);
                if (len != 0)
                {
                    dirX /= len;
                    dirY /= len;
                }
                posX += dirX;
                posY += dirY;

                if ((dirX == 0 && dirY == 0) || posX < 0 || posX >= mapSize - 1 || posY < 0 || posY >= mapSize - 1)
                {
                    break;
                }

                float newHeight   = CalculateHeightAndGradient(map, mapSize, posX, posY).height;
                float deltaHeight = newHeight - heightAndGradient.height;

                float sedimentCapacity = Mathf.Max(-deltaHeight * speed * water * sedimentCapacityFactor, minSedimentCapacity);

                if (sediment > sedimentCapacity || deltaHeight > 0)
                {
                    float amountToDeposit = (deltaHeight > 0) ? Mathf.Min(deltaHeight, sediment) : (sediment - sedimentCapacity) * depositSpeed;
                    sediment -= amountToDeposit;

                    map[dropletIndex]               += amountToDeposit * (1 - cellOffsetX) * (1 - cellOffsetY);
                    map[dropletIndex + 1]           += amountToDeposit * cellOffsetX * (1 - cellOffsetY);
                    map[dropletIndex + mapSize]     += amountToDeposit * (1 - cellOffsetX) * cellOffsetY;
                    map[dropletIndex + mapSize + 1] += amountToDeposit * cellOffsetX * cellOffsetY;
                }
                else
                {
                    float amountToErode = Mathf.Min((sedimentCapacity - sediment) * erodeSpeed, -deltaHeight);

                    for (int brushPointIndex = 0; brushPointIndex < erosionBrushIndices[dropletIndex].Length; brushPointIndex++)
                    {
                        int   nodeIndex          = erosionBrushIndices[dropletIndex][brushPointIndex];
                        float weighedErodeAmount = amountToErode * erosionBrushWeights[dropletIndex][brushPointIndex];
                        float deltaSediment      = (map[nodeIndex] < weighedErodeAmount) ? map[nodeIndex] : weighedErodeAmount;
                        map[nodeIndex] -= deltaSediment;
                        sediment       += deltaSediment;
                    }
                }

                speed  = Mathf.Sqrt(speed * speed + deltaHeight * gravity);
                water *= (1 - evaporateSpeed);
            }
        }
        return(map);
    }
Ejemplo n.º 5
0
    public void Erode(float[] map, int mapSize, int numIterations = 1, bool resetSeed = false)
    {
        Initialize(mapSize, resetSeed);

        for (int iteration = 0; iteration < numIterations; iteration++)
        {
            // Create water droplet at random point on map
            // Создание параметров капли воды, которая появится в случайной точке карты
            float posX     = prng.Next(0, mapSize - 1); // случайная точка по X
            float posY     = prng.Next(0, mapSize - 1); // случайная точка по Y
            float dirX     = 0;                         // Направление по X
            float dirY     = 0;                         // Направление по Y
            float speed    = initialSpeed;              // Инициализация скорости капли
            float water    = initialWaterVolume;        // Инициализация количества воды в капле
            float sediment = 0;                         // количество переносимого каплей осадка


            for (int lifetime = 0; lifetime < maxDropletLifetime; lifetime++)
            {
                int nodeX        = (int)posX;               // случайная позиция X
                int nodeY        = (int)posY;               // случайная позиция Y
                int dropletIndex = nodeY * mapSize + nodeX; // индекс капли
                // Calculate droplet's offset inside the cell (0,0) = at NW node, (1,1) = at SE node
                // Вычисление положения капли в одной клетке
                float cellOffsetX = posX - nodeX;
                float cellOffsetY = posY - nodeY;

                // Calculate droplet's height and direction of flow with bilinear interpolation of surrounding heights
                // Вычисление высоты капель и направления потока с билинейной интерполяцией окружающих высот
                HeightAndGradient heightAndGradient = CalculateHeightAndGradient(map, mapSize, posX, posY);

                // Update the droplet's direction and position (move position 1 unit regardless of speed)
                // Обновление направления и положения капель (перемещение позиции на 1 независимо от скорости)
                dirX = (dirX * inertia - heightAndGradient.gradientX * (1 - inertia));
                dirY = (dirY * inertia - heightAndGradient.gradientY * (1 - inertia));
                // Normalize direction
                float len = Mathf.Sqrt(dirX * dirX + dirY * dirY);
                if (len != 0)
                {
                    dirX /= len;
                    dirY /= len;
                }
                posX += dirX;
                posY += dirY;

                // Stop simulating droplet if it's not moving or has flowed over edge of map
                // Остановить моделирование капли, если она не движется или перетекла через край карты
                if ((dirX == 0 && dirY == 0) || posX < 0 || posX >= mapSize - 1 || posY < 0 || posY >= mapSize - 1)
                {
                    break;
                }

                // Find the droplet's new height and calculate the deltaHeight
                // Нахождение новой высоты капель и вычисление Дельта-высоты
                float newHeight   = CalculateHeightAndGradient(map, mapSize, posX, posY).height;
                float deltaHeight = newHeight - heightAndGradient.height;

                // Calculate the droplet's sediment capacity (higher when moving fast down a slope and contains lots of water)
                // Расчет емкости осадка капель (выше при быстром движении вниз по склону и содержит много воды)
                float sedimentCapacity = Mathf.Max(-deltaHeight * speed * water * sedimentCapacityFactor, minSedimentCapacity);

                // If carrying more sediment than capacity, or if flowing uphill:
                // Если несете больше наносов, чем емкость, или если течет в гору:
                if (sediment > sedimentCapacity || deltaHeight > 0)
                {
                    // If moving uphill (deltaHeight > 0) try fill up to the current height, otherwise deposit a fraction of the excess sediment
                    // При движении в гору (deltaHeight > 0) попробуйте заполнить до текущей высоты, в противном случае отложите часть избыточного осадка
                    float amountToDeposit = (deltaHeight > 0) ? Mathf.Min(deltaHeight, sediment) : (sediment - sedimentCapacity) * depositSpeed;
                    sediment -= amountToDeposit;

                    // Add the sediment to the four nodes of the current cell using bilinear interpolation
                    // Deposition is not distributed over a radius (like erosion) so that it can fill small pits
                    // Добавьте осадок в четыре узла текущей ячейки с помощью билинейной интерполяции
                    // Осаждение не распределяется по радиусу (как эрозия), так что он может заполнить небольшие ямы
                    map[dropletIndex]               += amountToDeposit * (1 - cellOffsetX) * (1 - cellOffsetY);
                    map[dropletIndex + 1]           += amountToDeposit * cellOffsetX * (1 - cellOffsetY);
                    map[dropletIndex + mapSize]     += amountToDeposit * (1 - cellOffsetX) * cellOffsetY;
                    map[dropletIndex + mapSize + 1] += amountToDeposit * cellOffsetX * cellOffsetY;
                }
                else
                {
                    // Erode a fraction of the droplet's current carry capacity.
                    // Clamp the erosion to the change in height so that it doesn't dig a hole in the terrain behind the droplet
                    // Выветривается часть капель тока, пропускная способность.
                    // Зажмите размывание к изменению в высоте так, что она не выкопает отверстие в местности за каплей
                    float amountToErode = Mathf.Min((sedimentCapacity - sediment) * erodeSpeed, -deltaHeight);

                    // Use erosion brush to erode from all nodes inside the droplet's erosion radius
                    // Используйте щетку размывания для того чтобы выветриться от всех узлов внутри радиуса размывания капелек
                    for (int brushPointIndex = 0; brushPointIndex < erosionBrushIndices[dropletIndex].Length; brushPointIndex++)
                    {
                        int   nodeIndex          = erosionBrushIndices[dropletIndex][brushPointIndex];
                        float weighedErodeAmount = amountToErode * erosionBrushWeights[dropletIndex][brushPointIndex];
                        float deltaSediment      = (map[nodeIndex] < weighedErodeAmount) ? map[nodeIndex] : weighedErodeAmount;
                        map[nodeIndex] -= deltaSediment;
                        sediment       += deltaSediment;
                    }
                }

                // Update droplet's speed and water content
                // Обновление скорости капель и содержания воды
                speed  = Mathf.Sqrt(speed * speed + deltaHeight * gravity);
                water *= (1 - evaporateSpeed);
            }
        }
    }