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); }
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; }