Пример #1
0
        HeightAndGradient CalculateHeightAndGradient(_7DaysToDie.Model.HeightMap heightMap, int mapSize, float posX, float posY)
        {
            int coordX = (int)posX;
            int coordY = (int)posY;

            // Calculate droplet's offset inside the cell (0,0) = at NW node, (1,1) = at SE node
            float x = posX - coordX;
            float y = posY - coordY;

            // Calculate heights of the four nodes of the droplet's cell
            int   nodeIndexNW = coordY * mapSize + coordX;
            float heightNW    = heightMap.Map[nodeIndexNW];
            float heightNE    = heightMap.Map[nodeIndexNW + 1];
            float heightSW    = heightMap.Map[nodeIndexNW + mapSize];
            float heightSE    = heightMap.Map[nodeIndexNW + mapSize + 1];

            // Calculate droplet's direction of flow with bilinear interpolation of height difference along the edges
            float gradientX = (heightNE - heightNW) * (1 - y) + (heightSE - heightSW) * y;
            float gradientY = (heightSW - heightNW) * (1 - x) + (heightSE - heightNE) * x;

            // Calculate height with bilinear interpolation of the heights of the nodes of the cell
            float height = heightNW * (1 - x) * (1 - y) + heightNE * x * (1 - y) + heightSW * (1 - x) * y + heightSE * x * y;

            return(new HeightAndGradient()
            {
                height = height, gradientX = gradientX, gradientY = gradientY
            });
        }
Пример #2
0
        public void Erode(_7DaysToDie.Model.HeightMap heightMap, 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(heightMap, 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 = (float)Math.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(heightMap, 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
                        heightMap.Map[dropletIndex]               += amountToDeposit * (1 - cellOffsetX) * (1 - cellOffsetY);
                        heightMap.Map[dropletIndex + 1]           += amountToDeposit * cellOffsetX * (1 - cellOffsetY);
                        heightMap.Map[dropletIndex + mapSize]     += amountToDeposit * (1 - cellOffsetX) * cellOffsetY;
                        heightMap.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 = (sedimentCapacity - sediment) * erodeSpeed;
                        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      = (heightMap.Map[nodeIndex] < weighedErodeAmount) ? heightMap.Map[nodeIndex] : weighedErodeAmount;
                            heightMap.Map[nodeIndex] -= deltaSediment;
                            sediment += deltaSediment;
                        }
                    }

                    // Update droplet's speed and water content
                    speed  = Mathf.Sqrt(speed * speed + deltaHeight * gravity);
                    water *= (1 - evaporateSpeed);
                }
            }
        }