Пример #1
0
        void applyErosion(ref Droplet d, ref float[] map, ref bool[] updated, float erosionAmount)
        {
            int radius = Vars.pErosionRadius;

            double[]  weights  = new double[(int)Math.Pow(2 * radius + 1, 2)];
            Vector2[] coords   = new Vector2[(int)Math.Pow(2 * radius + 1, 2)];
            int       numPoint = 0;

            double weighSum = 0;

            for (int x = -radius; x <= radius + 1; ++x)
            {
                for (int y = -radius; y <= radius + 1; ++y)
                {
                    int coordX = (int)d.x + x;
                    int coordY = (int)d.y + y;

                    float diffX = coordX - d.x, diffY = coordY - d.y;
                    float distanceSqrd = x * x + y * y;
                    // ignore points outside the circle
                    if (distanceSqrd > radius * radius)
                    {
                        continue;
                    }

                    if (coordX >= 0 && coordX < Vars.imgRes && coordY >= 0 && coordY < Vars.imgRes)
                    {
                        double weight = Math.Max(0, radius - Math.Sqrt(distanceSqrd));
                        weights[numPoint] = weight;
                        coords[numPoint]  = new Vector2(coordX, coordY);
                        numPoint++;
                        weighSum += weight;
                    }
                }
            }

            for (int i = 0; i < numPoint; ++i)
            {
                weights[i] /= weighSum;
                float pointErosion = (float)(erosionAmount * weights[i]);
                int   pointIndex   = (int)(coords[i].x * Vars.imgRes + coords[i].y);
                updated[pointIndex] = true;
                if (pointErosion > map[pointIndex])
                {
                    d.sediment     += map[pointIndex];
                    map[pointIndex] = 0;
                }
                else
                {
                    d.sediment      += pointErosion;
                    map[pointIndex] -= pointErosion;
                }
            }
        }
Пример #2
0
        void computeGradientHeight(float[] mapHeights, ref Droplet d, ref float dropletHeight, ref Vector2 gradient)
        {
            // get the upper left corner of the enclosing quad
            int xGrid = (int)Math.Floor(d.x), yGrid = (int)Math.Floor(d.y);

            // get droplet position inside the quad
            d.u = d.x - xGrid;
            d.v = d.y - yGrid;

            // get the heights of the quad corners
            float xy   = mapHeights[xGrid * Vars.imgRes + yGrid];
            float x1y  = mapHeights[(xGrid + 1) * Vars.imgRes + yGrid];
            float xy1  = mapHeights[xGrid * Vars.imgRes + yGrid + 1];
            float x1y1 = mapHeights[(xGrid + 1) * Vars.imgRes + yGrid + 1];

            // get droplet height by bilinear interpolation of the enclosing quad corners
            dropletHeight = (x1y1 * d.u + x1y * (1 - d.u)) * d.v + (xy1 * d.u + xy * (1 - d.v)) * (1 - d.v);
            gradient      = new Vector2(
                (x1y - xy) * (1 - d.v) + (x1y1 - xy1) * d.v,
                (xy1 - xy) * (1 - d.u) + (x1y1 - x1y) * d.u);
        }
Пример #3
0
        public void Update(int miniIts = 100, bool ret = false)
        {
            for (int i = 0; i < miniIts; i++)
            {
                Droplet d = new Droplet();
                for (int j = 0; j < Vars.nrIterations; j++)
                {
                    Vector2 oldPos = new Vector2(d.x, d.y);
                    int     xGrid = (int)Math.Floor(d.x), yGrid = (int)Math.Floor(d.y);
                    float   offsetX = d.x - xGrid;
                    float   offsetY = d.y - yGrid;
                    // get droplet height by bilinear interpolation of the enclosing quad
                    float   dHeight  = 0;
                    Vector2 gradient = new Vector2(0, 0);
                    computeGradientHeight(updatedHeights, ref d, ref dHeight, ref gradient);

                    // get new direction and normalize
                    d.dir.x = (d.dir.x * Vars.pInertia - gradient.x * (1 - Vars.pInertia));
                    d.dir.y = (d.dir.y * Vars.pInertia - gradient.y * (1 - Vars.pInertia));
                    d.dir.Normalize();
                    Console.WriteLine(d.dir);

                    // update position
                    d.x += d.dir.x;
                    d.y += d.dir.y;

                    // stop simulating droplet if it doesn't move or if it gets out of bounds
                    if ((d.dir.x == 0 && d.dir.y == 0) || d.x < 0 || d.x >= (Vars.imgRes - 1) || d.y < 0 || d.y >= (Vars.imgRes - 1))
                    {
                        break;
                    }

                    // compute new droplet height
                    float   newHeight   = 0;
                    Vector2 newGradient = new Vector2(0, 0);
                    computeGradientHeight(updatedHeights, ref d, ref newHeight, ref newGradient);

                    float heightDiff = newHeight - dHeight;
                    float c          = Math.Max(-heightDiff, Vars.pMinSlope) * d.velocity * d.water * Vars.pCapacity;

                    // droplet is moving uphill or has more sediment than its capacity
                    if (d.sediment > c)
                    {
                        // Deposit sediment
                        float depositAmount = (d.sediment - c) * Vars.pDeposition;
                        applyDeposition(ref d, ref updatedHeights, ref updatedPixel, depositAmount, newHeight);
                    }
                    else if (heightDiff > 0)
                    {
                        float depositAmount = Math.Min(heightDiff, d.sediment);
                        applyDeposition(ref d, ref updatedHeights, ref updatedPixel, depositAmount, dHeight, true, oldPos.x, oldPos.y);
                    }
                    else
                    {
                        // Erode all points inside the radius
                        float erosionAmount = Math.Min((c - d.sediment) * Vars.pErosion, -heightDiff);
                        applyErosion(ref d, ref updatedHeights, ref updatedPixel, erosionAmount);
                    }

                    // update droplet velocity and water amount
                    d.velocity = (float)Math.Sqrt(Math.Max(d.velocity * d.velocity - heightDiff * Vars.pGravity, 0));
                    d.water    = d.water * (1 - Vars.pEvaporation);
                }
                Vars.currentDroplets++;
            }

            if (ret)
            {
                updatedHeights = blurMap(updatedHeights, Vars.imgRes, updatedPixel);
                updatedPixel   = new bool[updatedHeights.Length];
            }
        }
Пример #4
0
        void applyDeposition(ref Droplet d, ref float[] map, ref bool[] updated, float depositAmount, float currHeight, bool tooHigh = false, float oldX = 0, float oldY = 0)
        {
            float X = d.x, Y = d.y;

            // We want to deposit before the hill, not on the hill
            if (tooHigh)
            {
                X = oldX;
                Y = oldY;
            }

            int            radius = Vars.pErosionRadius;
            List <double>  distanceWeights = new List <double>();
            List <double>  heightWeights = new List <double>();
            List <Vector2> coords = new List <Vector2>();
            int            numPoint = 0;

            double dWeightSum = 0, hWeightSum = 0;

            for (int x = -radius; x <= radius + 1; ++x)
            {
                for (int y = -radius; y <= radius + 1; ++y)
                {
                    int coordX = (int)X + x;
                    int coordY = (int)Y + y;

                    float diffX = coordX - X, diffY = coordY - Y;
                    float distanceSqrd = diffX * diffX + diffY * diffY;
                    // ignore points outside the circle
                    if (distanceSqrd > radius * radius)
                    {
                        continue;
                    }


                    if (coordX >= 0 && coordX < Vars.imgRes && coordY >= 0 && coordY < Vars.imgRes)
                    {
                        float hDiff = map[coordX * Vars.imgRes + coordY] - currHeight;

                        double distanceWeight = Math.Max(0, radius - Math.Sqrt(distanceSqrd));
                        distanceWeights.Add(distanceWeight);
                        // higher points recieve exponentially less deposition
                        double heightWeight = Math.Pow(1 - hDiff, 2);
                        heightWeights.Add(heightWeight);

                        coords.Add(new Vector2(coordX, coordY));
                        numPoint++;
                        dWeightSum += distanceWeight;
                        hWeightSum += heightWeight;
                    }
                }
            }

            for (int i = 0; i < numPoint; ++i)
            {
                distanceWeights[i] /= dWeightSum;
                heightWeights[i]   /= hWeightSum;
                double weight          = (distanceWeights[i] + heightWeights[i]) / 2;
                float  pointDeposition = (float)(depositAmount * weight);
                int    pointIndex      = (int)(coords[i].x * Vars.imgRes + coords[i].y);
                d.sediment         -= pointDeposition;
                map[pointIndex]    += pointDeposition;
                updated[pointIndex] = true;
            }
        }