public float[,] Process(float[,] sourceElevation, float minimumElevation, float xyScaleToMeters, float zScaleToMeters, float rainWaterAmount, float sedimentCapacity, float gravityConstant, float frictionConstant, float evaporationConstant, float depositionConstant, float dissolvingConstant, float stepDeltaTime, int finalBlurRadius)
    {
        UnityEngine.Debug.Log("*** Hydraulic Erosion Process ***");

        UnityEngine.Debug.Log("Gravity constant = " + gravityConstant + " m/s^2");
        UnityEngine.Debug.Log("Rain Water Amount = " + rainWaterAmount + " m^3");

        var stopwatch = new Stopwatch();

        stopwatch.Start();

        var parallelOptions = new ParallelOptions()
        {
            MaxDegreeOfParallelism = -1
        };

        m_outputElevationWidth     = sourceElevation.GetLength(1);
        m_outputElevationHeight    = sourceElevation.GetLength(0);
        m_outputElevationWidthMask = m_outputElevationWidth - 1;

        // remember constants
        m_minimumElevation = minimumElevation * zScaleToMeters;
        m_xyScaleToMeters  = xyScaleToMeters;
        m_zScaleToMeters   = zScaleToMeters;
        m_rainWaterAmount  = rainWaterAmount;
        m_sedimentCapacity = sedimentCapacity;

        m_gravityConstant     = gravityConstant;
        m_frictionConstant    = 1.0f - (frictionConstant * stepDeltaTime);
        m_evaporationConstant = 1.0f - (evaporationConstant * stepDeltaTime);
        m_depositionConstant  = depositionConstant * stepDeltaTime;
        m_dissolvingConstant  = dissolvingConstant * stepDeltaTime;

        m_stepDeltaTime = stepDeltaTime;

        // allocate terrain level buffer
        m_outputElevation = new float[m_outputElevationHeight, m_outputElevationWidth];

        // initialize terrain level buffer
        for (var y = 0; y < m_outputElevationHeight; y++)
        {
            for (var x = 0; x < m_outputElevationWidth; x++)
            {
                m_outputElevation[y, x] = sourceElevation[y, x] * zScaleToMeters;
            }
        }

        UnityEngine.Debug.Log("Initialize Terrain Level - " + stopwatch.ElapsedMilliseconds + " milliseconds");

        stopwatch.Restart();

        // perlin noise for generating the twist buffer
        var noise = new SeamlessNoise(50, 256, 1);

        // allocate twist buffer
        m_twistBuffer = new int[m_outputElevationHeight, m_outputElevationWidth];

        // initialize twist buffer
        Parallel.For(0, m_outputElevationHeight, parallelOptions, y =>
        {
            for (var x = 0; x < m_outputElevationWidth; x++)
            {
                var sample = noise.Perlin(0, 256, x * 128.0f / m_outputElevationWidth, y * 64.0f / m_outputElevationHeight);

                m_twistBuffer[y, x] = Mathf.RoundToInt(sample * 31.0f);
            }
        });

        UnityEngine.Debug.Log("Generate Twist Buffer - " + stopwatch.ElapsedMilliseconds + " milliseconds");

        stopwatch.Restart();

        // allocate direction vector buffer
        m_directionVector = new Vector3[512];

        // initialize direction vector buffer
        for (int i = 0; i < 512; i++)
        {
            var angle = i * Mathf.PI * 2.0f / 512.0f;

            var x = Mathf.Sin(angle) * m_xyScaleToMeters;
            var y = Mathf.Cos(angle) * m_xyScaleToMeters;

            m_directionVector[i] = new Vector3(x, 0.0f, y);
        }

        UnityEngine.Debug.Log("Generate Direction Vectors - " + stopwatch.ElapsedMilliseconds + " milliseconds");

        stopwatch.Restart();

        // allocate, initialize, and shuffle random xy field
        var randomXYSize = m_outputElevationHeight / 2;

        var randomXYSizeSquared = randomXYSize * randomXYSize;

        var randomXY = new Vector2Int[randomXYSizeSquared];

        for (var y = 0; y < randomXYSize; y++)
        {
            for (var x = 0; x < randomXYSize; x++)
            {
                randomXY[y * randomXYSize + x] = new Vector2Int(x, y);
            }
        }

        Random.InitState(100);

        for (var i = 0; i < randomXYSizeSquared; i++)
        {
            int x = Random.Range(i, randomXYSizeSquared);

            var tmp = randomXY[i];

            randomXY[i] = randomXY[x];
            randomXY[x] = tmp;
        }

        UnityEngine.Debug.Log("Generate Random XY - " + stopwatch.ElapsedMilliseconds + " milliseconds");

        stopwatch.Restart();

        // do erosion steps
        var snapshotInterval = 2048;
        var snapshotSteps    = randomXYSizeSquared / snapshotInterval;

        for (var step = 0; step < snapshotSteps; step++)
        {
            // show progress bar
            if (EditorUtility.DisplayCancelableProgressBar("Planet Generator", "Making it rain...", (float)step / (snapshotSteps - 1)))
            {
                return(null);
            }

            var offset = step * snapshotInterval;

            Parallel.For(0, 8, parallelOptions, j =>
            {
                var dx = randomXYSize * (j % 4);
                var dy = randomXYSize * (j / 4);

                for (var i = 0; i < snapshotInterval; i++)
                {
                    var x = randomXY[i + offset].x + dx;
                    var y = randomXY[i + offset].y + dy;

                    var drop = new Drop(x, y, m_rainWaterAmount, 0.0f, Vector3.zero);

                    while (drop.Update())
                    {
                    }
                }
            });
        }

        UnityEngine.Debug.Log("Erosion Steps - " + stopwatch.ElapsedMilliseconds + " milliseconds");

        stopwatch.Restart();

        // remove original elevation and remove scale (prep for blur)
        var inverseScaleToMeters = 1.0f / m_zScaleToMeters;

        for (var y = 0; y < m_outputElevationHeight; y++)
        {
            for (var x = 0; x < m_outputElevationWidth; x++)
            {
                var newElevation = Mathf.Max(0.0f, m_outputElevation[y, x] * inverseScaleToMeters);

                var delta = newElevation - sourceElevation[y, x];

                m_outputElevation[y, x] = Mathf.Min(0.0f, delta);
            }
        }

        UnityEngine.Debug.Log("Convert to Height Deltas - " + stopwatch.ElapsedMilliseconds + " milliseconds");

        stopwatch.Restart();

        // pole smoothing
        for (var y = 0; y < m_outputElevationHeight; y++)
        {
            var p1 = m_outputElevationHeight * 0.1f;
            var p2 = m_outputElevationHeight * 0.2f;
            var p3 = m_outputElevationHeight * 0.8f;
            var p4 = m_outputElevationHeight * 0.9f;

            var poleMultiplier = Mathf.SmoothStep(0.0f, 1.0f, (y - p1) / (p2 - p1)) * Mathf.SmoothStep(1.0f, 0.0f, (y - p3) / (p4 - p3));

            for (var x = 0; x < m_outputElevationWidth; x++)
            {
                m_outputElevation[y, x] *= poleMultiplier;
            }
        }

        UnityEngine.Debug.Log("Pole Smoothing - " + stopwatch.ElapsedMilliseconds + " milliseconds");

        stopwatch.Restart();

        // final blur to get rid of the jaggies in the delta heights
        var gaussianBlur = new PG_GaussianBlurElevation();

        m_outputElevation = gaussianBlur.Process(m_outputElevation, finalBlurRadius, finalBlurRadius);

        // put original elevation back in
        for (var y = 0; y < m_outputElevationHeight; y++)
        {
            for (var x = 0; x < m_outputElevationWidth; x++)
            {
                m_outputElevation[y, x] += sourceElevation[y, x];
            }
        }

        UnityEngine.Debug.Log("Final Blur - " + stopwatch.ElapsedMilliseconds + " milliseconds");

        // return the processed buffer
        return(m_outputElevation);
    }
Beispiel #2
0
    public void AsyncProcess(byte[] bytes)
    {
        var progressStepSize = 0.5f / 12.0f;

        m_progress += progressStepSize;

        // decompress the planet data
        using (var memoryStream = new MemoryStream(bytes))
        {
            using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress, false))
            {
                var binaryReader = new BinaryReader(gZipStream);

                var version = binaryReader.ReadInt32();

                if (version != c_versionNumber)
                {
                    m_abort = true;
                }
                else
                {
                    m_minimumElevation = binaryReader.ReadSingle();
                    m_waterElevation   = binaryReader.ReadSingle();
                    m_snowElevation    = binaryReader.ReadSingle();

                    var r = binaryReader.ReadSingle();
                    var g = binaryReader.ReadSingle();
                    var b = binaryReader.ReadSingle();

                    m_waterColor = new Color(r, g, b);

                    r = binaryReader.ReadSingle();
                    g = binaryReader.ReadSingle();
                    b = binaryReader.ReadSingle();

                    m_groundColor = new Color(r, g, b);

                    r = binaryReader.ReadSingle();
                    g = binaryReader.ReadSingle();
                    b = binaryReader.ReadSingle();

                    m_snowColor = new Color(r, g, b);

                    var preparedMapWidth  = binaryReader.ReadInt32();
                    var preparedMapHeight = binaryReader.ReadInt32();

                    m_preparedHeightMap = new float[preparedMapHeight, preparedMapWidth];

                    for (var y = 0; y < preparedMapHeight; y++)
                    {
                        for (var x = 0; x < preparedMapWidth; x++)
                        {
                            m_preparedHeightMap[y, x] = binaryReader.ReadSingle();
                        }
                    }

                    m_preparedColorMap = new Color[preparedMapHeight, preparedMapWidth];

                    for (var y = 0; y < preparedMapHeight; y++)
                    {
                        for (var x = 0; x < preparedMapWidth; x++)
                        {
                            r = binaryReader.ReadSingle();
                            g = binaryReader.ReadSingle();
                            b = binaryReader.ReadSingle();

                            m_preparedColorMap[y, x] = new Color(r, g, b);
                        }
                    }

                    if (m_planet.IsGasGiant())
                    {
                        m_textureMapWidth  = c_gasGiantTextureMapWidth;
                        m_textureMapHeight = c_gasGiantTextureMapHeight;
                    }
                    else
                    {
                        m_textureMapWidth  = c_nonGasGiantTextureMapWidth;
                        m_textureMapHeight = c_nonGasGiantTextureMapHeight;

                        m_minimumDifference = binaryReader.ReadSingle();
                        m_maximumDifference = binaryReader.ReadSingle();

                        var differenceBufferSize = m_textureMapWidth * m_textureMapHeight;

                        m_differenceBuffer = new byte[differenceBufferSize];

                        gZipStream.Read(m_differenceBuffer, 0, differenceBufferSize);
                    }
                }
            }
        }

        // gas giant or not?
        if (m_planet.IsGasGiant())
        {
            // yes - do color bicubic scale
            m_progress += progressStepSize;

            var bicubicScaleColor = new PG_BicubicScaleColor();

            m_albedoMap = bicubicScaleColor.Process(m_preparedColorMap, m_textureMapWidth, m_textureMapHeight);

            // do color gaussian blur (this becomes our albedo map)
            m_progress += progressStepSize;

            var gaussianBlurColor = new PG_GaussianBlurColor();

            m_albedoMap = gaussianBlurColor.Process(m_albedoMap, c_xBlurRadiusGasGiant, c_yBlurRadiusGasGiant);

            // do elevation bicubic scale
            m_progress += progressStepSize;

            var bicubicScaleElevation = new PG_BicubicScaleElevation();

            m_elevation = bicubicScaleElevation.Process(m_preparedHeightMap, m_textureMapWidth, m_textureMapHeight);

            // do elevation gaussian blur
            m_progress += progressStepSize;

            var gaussianBlurElevation = new PG_GaussianBlurElevation();

            m_elevation = gaussianBlurElevation.Process(m_elevation, c_xBlurRadiusGasGiant, c_yBlurRadiusGasGiant);

            // build specular map
            m_progress += progressStepSize;

            m_specularMap = new Color[m_textureMapHeight, m_textureMapWidth];

            for (var y = 0; y < m_textureMapHeight; y++)
            {
                for (var x = 0; x < m_textureMapWidth; x++)
                {
                    var elevation = m_elevation[y, x];

                    m_specularMap[y, x] = new Color(elevation, elevation, elevation, 0.25f);
                }
            }

            // build water mask map
            m_progress += progressStepSize;

            m_waterMaskMap = new Color[4, 4];

            for (var y = 0; y < 4; y++)
            {
                for (var x = 0; x < 4; x++)
                {
                    m_waterMaskMap[y, x] = Color.black;
                }
            }

            // build normal map
            m_progress += progressStepSize;

            m_normalMap = new Color[4, 4];

            var defaultNormal = new Color(0.5f, 0.5f, 1.0f);

            for (var y = 0; y < 4; y++)
            {
                for (var x = 0; x < 4; x++)
                {
                    m_normalMap[y, x] = defaultNormal;
                }
            }
        }
        else
        {
            // do elevation bicubic scale
            m_progress += progressStepSize;

            var bicubicScaleElevation = new PG_BicubicScaleElevation();

            m_elevation = bicubicScaleElevation.Process(m_preparedHeightMap, m_textureMapWidth, m_textureMapHeight);

            // do craters
            m_progress += progressStepSize;

            if (m_planet.m_atmosphereDensityId == 0)
            {
                var craters = new PG_Craters();

                m_elevation = craters.Process(m_elevation, m_planet.m_id, 0.1f, m_waterElevation);
            }

            // factor in elevation difference map
            m_progress += progressStepSize;

            m_maximumElevation = 0.0f;

            var elevationScale = (m_maximumDifference - m_minimumDifference) / 255.0f;

            for (var y = 0; y < m_textureMapHeight; y++)
            {
                for (var x = 0; x < m_textureMapWidth; x++)
                {
                    var difference = m_differenceBuffer[y * m_textureMapWidth + x];

                    m_elevation[y, x] += (difference * elevationScale) + m_minimumDifference;

                    m_maximumElevation = Mathf.Max(m_maximumElevation, m_elevation[y, x]);
                }
            }

            // build albedo map
            m_progress += progressStepSize;

            var albedoMap = new PG_AlbedoMap();

            m_albedoMap = albedoMap.Process(m_elevation, m_preparedColorMap, m_waterElevation, m_waterColor, m_groundColor);

            // build specular map
            m_progress += progressStepSize;

            var waterSpecularColor = m_planet.IsMolten() ? new Color(0.75f, 0.125f, 0.125f) : new Color(1.0f, 1.0f, 1.0f);

            var waterSpecularPower = m_planet.IsMolten() ? 0.4f : 0.75f;

            var specularMap = new PG_SpecularMap();

            m_specularMap = specularMap.Process(m_elevation, m_albedoMap, m_waterElevation, waterSpecularColor, waterSpecularPower, 4);

            // build water mask map
            m_progress += progressStepSize;

            var waterMaskMap = new PG_WaterMaskMap();

            m_waterMaskMap = waterMaskMap.Process(m_elevation, m_waterElevation, 4);

            // build normal map
            m_progress += progressStepSize;

            var normalMap = new PG_NormalMap();

            m_normalMap = normalMap.Process(m_elevation, c_normalScale, m_waterElevation, 2);
        }

        // get albedo pixels
        m_progress += progressStepSize;

        m_albedoPixels = new Color[m_textureMapWidth * m_textureMapHeight];

        var index = 0;

        for (var y = 0; y < m_textureMapHeight; y++)
        {
            for (var x = 0; x < m_textureMapWidth; x++)
            {
                m_albedoPixels[index++] = m_albedoMap[y, x];
            }
        }

        // get specular pixels
        m_progress += progressStepSize;

        var textureMapWidth  = m_specularMap.GetLength(1);
        var textureMapHeight = m_specularMap.GetLength(0);

        m_specularPixels = new Color[textureMapWidth * textureMapHeight];

        index = 0;

        for (var y = 0; y < textureMapHeight; y++)
        {
            for (var x = 0; x < textureMapWidth; x++)
            {
                m_specularPixels[index++] = m_specularMap[y, x];
            }
        }

        // get water mask pixels
        m_progress += progressStepSize;

        textureMapWidth  = m_waterMaskMap.GetLength(1);
        textureMapHeight = m_waterMaskMap.GetLength(0);

        m_waterMaskPixels = new Color[textureMapWidth * textureMapHeight];

        index = 0;

        for (var y = 0; y < textureMapHeight; y++)
        {
            for (var x = 0; x < textureMapWidth; x++)
            {
                m_waterMaskPixels[index++] = m_waterMaskMap[y, x];
            }
        }

        // get normal pixels
        m_progress += progressStepSize;

        textureMapWidth  = m_normalMap.GetLength(1);
        textureMapHeight = m_normalMap.GetLength(0);

        m_normalPixels = new Color[textureMapWidth * textureMapHeight];

        index = 0;

        for (var y = 0; y < textureMapHeight; y++)
        {
            for (var x = 0; x < textureMapWidth; x++)
            {
                m_normalPixels[index++] = new Color(0.0f, m_normalMap[y, x].g, 0.0f, m_normalMap[y, x].r);
            }
        }

        m_step = 20;
    }