Пример #1
0
        static public void ErosionIteration(Matrix heights, Matrix erosion, Matrix sedimentSum, CoordRect area = new CoordRect(),
                                            float erosionDurability = 0.9f, float erosionAmount = 1f, float sedimentAmount = 0.5f, int erosionFluidityIterations = 3, float ruffle = 0.1f,
                                            Matrix torrents         = null, Matrix sediments    = null, int[] stepsArray = null, int[] heightsInt = null, int[] order = null)
        {
            if (area.isZero)
            {
                area = heights.rect;
            }
            int count = heights.count;

            #region Creating order array

            int steps = 1000000;

            //creating int-type copy of heights
            if (heightsInt == null)
            {
                heightsInt = new int[count];
            }
            for (int i = 0; i < heights.count; i++)
            {
                heightsInt[i] = (int)(Mathf.Clamp01(heights.array[i]) * steps);                                //TODO replace fn call
            }
            //order array
            if (order == null)
            {
                order = new int[count];
            }
            order = ArrayTools.Order(heightsInt, order: order, max: heights.count, stepsArray: stepsArray);


            /*if (order==null) order = new int[count];
             * for (int i=0; i<order.Length; i++) order[i] = i;
             * float[] refHeights = new float[heights.array.Length];
             * Array.Copy(heights.array, refHeights, heights.array.Length);
             * Array.Sort(refHeights, order);*/

            //setting out-of-area order num to negative
            for (int j = 0; j < heights.count; j++)
            {
                int   pos   = order[j];
                Coord coord = heights.rect.CoordByNum(pos);
                if (!area.CheckInRangeAndBounds(coord))
                {
                    order[j] = -1;
                }
            }


            #endregion

            #region Creating torrents

            if (torrents == null)
            {
                torrents = new Matrix(heights.rect);
            }
            torrents.ChangeRect(heights.rect);
            torrents.Fill(1f);                             //casting initial rain

            for (int j = count - 1; j >= 0; j--)
            {
                //finding column ordered by height
                int pos = order[j];
                if (pos < 0)
                {
                    continue;
                }

                /*
                 * MooreCross height = new MooreCross(heights, pos); //moore
                 * MooreCross torrent = new MooreCross(torrents, pos); //moore
                 * if (torrent.c > 2000000000) torrent.c = 2000000000;
                 *
                 * //creating torrents
                 * MooreCross delta = new MooreCross(height); //moore
                 * delta.SubtractInverse(height.c);
                 * delta.ClampPositive();
                 *
                 * delta.Percent(); //every side now determines a percent - how many water should go to it
                 * delta.Multiply(torrent.c);
                 * torrent.Add(delta);
                 */

                //getting height values
                float[] m = heights.array; int i = pos; int sizeX = heights.rect.size.x;
                float   h = m[i]; float hx = m[i - 1]; float hX = m[i + 1]; float hz = m[i - sizeX]; float hZ = m[i + sizeX];
                float   hxz = m[i - 1 - sizeX]; float hXz = m[i + 1 - sizeX]; float hxZ = m[i - 1 + sizeX]; float hXZ = m[i + 1 + sizeX];

                //creating delta
                float d = h - h; float dx = h - hx; float dX = h - hX; float dz = h - hz; float dZ = h - hZ;
                float dxz = h - hxz; float dXz = h - hXz; float dxZ = h - hxZ; float dXZ = h - hXZ;

                //ignoring negative delta values
                d   = d > 0? d : 0; dx = dx > 0? dx : 0; dX = dX > 0? dX : 0; dz = dz > 0? dz : 0; dZ = dZ > 0? dZ : 0;
                dxz = dxz > 0? dxz : 0; dXz = dXz > 0? dXz : 0; dxZ = dxZ > 0? dxZ : 0; dXZ = dXZ > 0? dXZ : 0;

                //finding percents
                float p = 0, px = 0, pX = 0, pz = 0, pZ = 0, pxz = 0, pXz = 0, pxZ = 0, pXZ = 0;
                float s = d + dx + dX + dz + dZ + dxz + dXz + dxZ + dXZ;
                if (s > 0.00001f)
                {
                    p = d / s; px = dx / s; pX = dX / s; pz = dz / s; pZ = dZ / s; pxz = dxz / s; pXz = dXz / s; pxZ = dxZ / s; pXZ = dXZ / s;
                }

                //getting central torrent (and clamping it)
                float t = torrents.array[i];
                if (t > 2000000000)
                {
                    t = 2000000000;
                }

                //spreading central torrent according percents delta. And adding to torrents array
                m                 = torrents.array;
                m[i]             += t * p; m[i - 1] += t * px; m[i + 1] += t * pX; m[i - sizeX] += t * pz; m[i + sizeX] += t * pZ;
                m[i - 1 - sizeX] += t * pxz; m[i + 1 - sizeX] += t * pXz; m[i - 1 + sizeX] += t * pxZ; m[i + 1 + sizeX] += t * pXZ;
            }
            #endregion

            #region Erosion

            if (sediments == null)
            {
                sediments = new Matrix(heights.rect);
            }
            else
            {
                sediments.ChangeRect(heights.rect);
            }
            sediments.Clear();

            for (int j = count - 1; j >= 0; j--)
            {
                //finding column ordered by height
                int pos = order[j];
                if (pos < 0)
                {
                    continue;
                }

                /*
                 * Cross height = new Cross(heights, pos);
                 * Cross torrent = new Cross(torrents, pos);
                 * Cross sediment = new Cross(sediments, pos);
                 *
                 * //erosion
                 * float erodeLine = (height.c + height.min)/2f; //halfway between current and maximum height
                 *
                 * if (height.c > erodeLine) //raising soil if column is higher than eroded column
                 * {
                 *      float raised = height.c - erodeLine;
                 *      raised = Mathf.Min(raised, raised*(torrent.c-1) * (1-erosionDurability));  //could not raise more soil than height-minHeight. //torrents always have 1 or more
                 *      raised *= erosionAmount;
                 *
                 *      heights.array[pos] -= raised; //raising soil
                 *      height.c -= raised;
                 *      sediments.array[pos] += raised * sedimentAmount; //and saving raised to sediment
                 *      sediment.c += raised * sedimentAmount;
                 *
                 *      if (paintErosion != null) paintErosion.array[pos] += raised * mapsFactor; //and writing to ref
                 * }
                 */

                //getting height values
                float[] m = heights.array; int i = pos; int sizeX = heights.rect.size.x;
                float   h = m[i]; float hx = m[i - 1]; float hX = m[i + 1]; float hz = m[i - sizeX]; float hZ = m[i + sizeX];

                //height minimum
                float h_min = h;
                if (hx < h_min)
                {
                    h_min = hx;
                }
                if (hX < h_min)
                {
                    h_min = hX;
                }
                if (hz < h_min)
                {
                    h_min = hz;
                }
                if (hZ < h_min)
                {
                    h_min = hZ;
                }

                //erosion line
                float erodeLine = (h + h_min) / 2f;                               //halfway between current and maximum height
                if (h < erodeLine)
                {
                    continue;
                }

                //raising soil
                float raised    = h - erodeLine;
                float maxRaised = raised * (torrents.array[pos] - 1) * (1 - erosionDurability);
                if (raised > maxRaised)
                {
                    raised = maxRaised;
                }
                raised *= erosionAmount;

                //saving arrays
                heights.array[pos]   -= raised;
                sediments.array[pos] += raised * sedimentAmount;
                //sedimentSum.array[pos] += sediments.array[pos] * 10;
                if (erosion != null)
                {
                    erosion.array[pos] += raised;                                                  //and writing to ref
                }
            }
            #endregion



            #region Settling sediment

            for (int l = 0; l < erosionFluidityIterations; l++)
            {
                for (int j = count - 1; j >= 0; j--)
                {
                    //finding column ordered by height
                    int pos = order[j];
                    if (pos < 0)
                    {
                        continue;
                    }

                    //getting height values
                    float[] m = heights.array; int sizeX = heights.rect.size.x;
                    float   h = m[pos]; float hx = m[pos - 1]; float hX = m[pos + 1]; float hz = m[pos - sizeX]; float hZ = m[pos + sizeX];

                    //getting sediment values
                    m = sediments.array;
                    float s = m[pos]; float sx = m[pos - 1]; float sX = m[pos + 1]; float sz = m[pos - sizeX]; float sZ = m[pos + sizeX];

                    //sediment sum
                    float sum = s + sx + sX + sz + sZ;
                    if (sum < 0.00001f)
                    {
                        continue;
                    }

                    //pouring sum to all cells
                    float sedimentFifth = sum / 5;
                    s = sedimentFifth; sx = sedimentFifth; sX = sedimentFifth; sz = sedimentFifth; sZ = sedimentFifth;

                    //levelling
                    //for (int i=0; i<2; i++)
                    //{
                    //x line
                    float avg = (h + s + sx + hx) / 2;
                    if (h + s > hx + sx)
                    {
                        float transfer = s + h - avg; if (transfer > s)
                        {
                            transfer = s;
                        }
                        s -= transfer; sx += transfer;
                    }
                    else
                    {
                        float transfer = sx + hx - avg; if (transfer > sx)
                        {
                            transfer = sx;
                        }
                        sx -= transfer; s += transfer;
                    }

                    avg = (hx + sx + sX + hX) / 2;
                    if (hx + sx > hX + sX)
                    {
                        float transfer = sx + hx - avg; if (transfer > sx)
                        {
                            transfer = sx;
                        }
                        sx -= transfer; sX += transfer;
                    }
                    else
                    {
                        float transfer = sX + hX - avg; if (transfer > sX)
                        {
                            transfer = sX;
                        }
                        sX -= transfer; sx += transfer;
                    }

                    avg = (h + s + sX + hX) / 2;
                    if (h + s > hX + sX)
                    {
                        float transfer = s + h - avg; if (transfer > s)
                        {
                            transfer = s;
                        }
                        s -= transfer; sX += transfer;
                    }
                    else
                    {
                        float transfer = sX + hX - avg; if (transfer > sX)
                        {
                            transfer = sX;
                        }
                        sX -= transfer; s += transfer;
                    }

                    //z line
                    avg = (h + s + sz + hz) / 2;
                    if (h + s > hz + sz)
                    {
                        float transfer = s + h - avg; if (transfer > s)
                        {
                            transfer = s;
                        }
                        s -= transfer; sz += transfer;
                    }
                    else
                    {
                        float transfer = sz + hz - avg; if (transfer > sz)
                        {
                            transfer = sz;
                        }
                        sz -= transfer; s += transfer;
                    }

                    avg = (hZ + sZ + sz + hz) / 2;
                    if (hZ + sZ > hz + sz)
                    {
                        float transfer = sZ + hZ - avg; if (transfer > sZ)
                        {
                            transfer = sZ;
                        }
                        sZ -= transfer; sz += transfer;
                    }
                    else
                    {
                        float transfer = sz + hz - avg; if (transfer > sz)
                        {
                            transfer = sz;
                        }
                        sz -= transfer; sZ += transfer;
                    }

                    avg = (h + s + sz + hz) / 2;
                    if (h + s > hz + sz)
                    {
                        float transfer = s + h - avg; if (transfer > s)
                        {
                            transfer = s;
                        }
                        s -= transfer; sz += transfer;
                    }
                    else
                    {
                        float transfer = sz + hz - avg; if (transfer > sz)
                        {
                            transfer = sz;
                        }
                        sz -= transfer; s += transfer;
                    }

                    //side pairs
                    avg = (hx + sx + sz + hz) / 2;
                    if (hx + sx > hz + sz)
                    {
                        float transfer = sx + hx - avg; if (transfer > sx)
                        {
                            transfer = sx;
                        }
                        sx -= transfer; sz += transfer;
                    }
                    else
                    {
                        float transfer = sz + hz - avg; if (transfer > sz)
                        {
                            transfer = sz;
                        }
                        sz -= transfer; sx += transfer;
                    }

                    avg = (hX + sX + sZ + hZ) / 2;
                    if (hX + sX > hZ + sZ)
                    {
                        float transfer = sX + hX - avg; if (transfer > sX)
                        {
                            transfer = sX;
                        }
                        sX -= transfer; sZ += transfer;
                    }
                    else
                    {
                        float transfer = sZ + hZ - avg; if (transfer > sZ)
                        {
                            transfer = sZ;
                        }
                        sZ -= transfer; sX += transfer;
                    }

                    avg = (hx + sx + sZ + hZ) / 2;
                    if (hx + sx > hZ + sZ)
                    {
                        float transfer = sx + hx - avg; if (transfer > sx)
                        {
                            transfer = sx;
                        }
                        sx -= transfer; sZ += transfer;
                    }
                    else
                    {
                        float transfer = sZ + hZ - avg; if (transfer > sZ)
                        {
                            transfer = sZ;
                        }
                        sZ -= transfer; sx += transfer;
                    }

                    avg = (hX + sX + sz + hz) / 2;
                    if (hX + sX > hz + sz)
                    {
                        float transfer = sX + hX - avg; if (transfer > sX)
                        {
                            transfer = sX;
                        }
                        sX -= transfer; sz += transfer;
                    }
                    else
                    {
                        float transfer = sz + hz - avg; if (transfer > sz)
                        {
                            transfer = sz;
                        }
                        sz -= transfer; sX += transfer;
                    }
                    //}

                    //to matrix
                    m      = sediments.array;
                    m[pos] = s; m[pos - 1] = sx; m[pos + 1] = sX; m[pos - sizeX] = sz; m[pos + sizeX] = sZ;

                    if (sedimentSum != null)
                    {
                        m       = sedimentSum.array;
                        m[pos] += s; m[pos - 1] += sx; m[pos + 1] += sX; m[pos - sizeX] += sz; m[pos + sizeX] += sZ;
                    }
                }
            }
            #endregion

            #region Writing sediments back to height, adding smooth and ruffle

            int seed = 12345;
            for (int j = heights.array.Length - 1; j >= 0; j--)
            {
                //writing heights
                heights.array[j] += sediments.array[j];

                seed = 214013 * seed + 2531011;
                float random = ((seed >> 16) & 0x7FFF) / 32768f;

                int pos = order[j];
                if (pos < 0)
                {
                    continue;
                }

                float[] m = heights.array; int sizeX = heights.rect.size.x;
                float   h = m[pos]; float hx = m[pos - 1]; float hX = m[pos + 1]; float hz = m[pos - sizeX]; float hZ = m[pos + sizeX];

                //smoothing sediments a bit
                float s = sediments.array[pos];
                if (s > 0.0001f)
                {
                    float smooth = s / 2f; if (smooth > 0.75f)
                    {
                        smooth = 0.75f;
                    }
                    heights.array[pos] = h * (1 - smooth) + (hx + hX + hz + hZ) / 4f * smooth;
                }

                else
                {
                    float maxHeight = hx; if (hX > maxHeight)
                    {
                        maxHeight = hX;
                    }
                    if (hz > maxHeight)
                    {
                        maxHeight = hz;
                    }
                    if (hZ > maxHeight)
                    {
                        maxHeight = hZ;
                    }
                    float minHeight = hx; if (hX < minHeight)
                    {
                        minHeight = hX;
                    }
                    if (hz < minHeight)
                    {
                        minHeight = hz;
                    }
                    if (hZ < minHeight)
                    {
                        minHeight = hZ;
                    }
                    float randomHeight = random * (maxHeight - minHeight) + minHeight;
                    heights.array[pos] = heights.array[pos] * (1 - ruffle) + randomHeight * ruffle;
                }
                //Cross height = new Cross(heights, pos);
                //float maxAround = Mathf.Max(heights.array[pos-1], heights.array[pos+1], heights.array[pos-heights.rect.size.x], heights.array[pos+heights.rect.size.x]);
                //heights.array[pos] = Mathf.Min(heights.array[pos], maxAround);

                //heights.array[pos] = Mathf.Min(height.c, height.maxAround);
                //heights.array[pos] = Mathf.Min(height.c, height.avgAround)*0.5f + height.c*0.5f;

                //float maxHeight = height.maxAround;
                //float minHeight = height.minAround;
                //float randomHeight = random*(maxHeight-minHeight) + minHeight;
                //heights.array[pos] = heights.array[pos]*(1-ruffle) + randomHeight*ruffle;
            }

            #endregion
        }                //erosion iteration