public void processTerrain(ErosionProgressDelegate erosionProgressDelegate) { Terrain ter = (Terrain)GetComponent (typeof(Terrain)); if (ter == null) { return; } try { // Pass the height array to the erosion script TerrainData terData = ter.terrainData; Tx = terData.heightmapWidth; Ty = terData.heightmapHeight; heightMap = terData.GetHeights (0, 0, Tx, Ty); waterMap = new float[Tx, Ty]; sedimentMap = new float[Tx, Ty]; waterErosion (waterErosionIterations, erosionProgressDelegate); // Apply it to the terrain object terData.SetHeights (0, 0, heightMap); } catch (Exception e) { Debug.LogError ("An error occurred: " + e); } }
void waterErosion(int iterations, ErosionProgressDelegate erosionProgressDelegate) { for (int iter = 0; iter < iterations; iter++) { float percentComplete = (float)iter / (float)iterations; erosionProgressDelegate("Applying Water Erosion", "Applying water erosion.", iter, iterations, percentComplete); for (int y = 0; y < Ty; y++) { for (int x = 0; x < Tx; x++) { waterMap [x, y] = waterErosionRainfall; sedimentMap [x, y] = 0.0f; } } int waterCounter = 100; do { waterExists = false; for (int y = 0; y < Ty; y++) { for (int x = 0; x < Tx; x++) { moveWater(x, y); } } waterCounter--; } while (waterExists && (waterCounter > 0)); for (int y = 0; y < Ty; y++) { for (int x = 0; x < Tx; x++) { addSediment(x, y); } } } }
public void processTerrain(ErosionProgressDelegate erosionProgressDelegate) { Terrain ter = (Terrain)GetComponent(typeof(Terrain)); if (ter == null) { return; } try { // Pass the height array to the erosion script TerrainData terData = ter.terrainData; Tx = terData.heightmapWidth; Ty = terData.heightmapHeight; heightMap = terData.GetHeights(0, 0, Tx, Ty); waterMap = new float[Tx, Ty]; sedimentMap = new float[Tx, Ty]; waterErosion(waterErosionIterations, erosionProgressDelegate); // Apply it to the terrain object terData.SetHeights(0, 0, heightMap); } catch (Exception e) { Debug.LogError("An error occurred: " + e); } }
public void FullHydraulicErosion(int iterations, float rainfall, float evaporation, float solubility, float saturation) { erosionTypeInt = 1; erosionType = ErosionType.Hydraulic; hydraulicTypeInt = 1; hydraulicType = HydraulicType.Full; hydraulicIterations = iterations; hydraulicRainfall = rainfall; hydraulicEvaporation = evaporation; hydraulicSedimentSolubility = solubility; hydraulicSedimentSaturation = saturation; neighbourhood = Neighbourhood.Moore; ErosionProgressDelegate erosionProgressDelegate = new ErosionProgressDelegate(dummyErosionProgress); erodeAllTerrain(erosionProgressDelegate); }
// -------------------------------------------------------------------------------------------------------- API FUNCTIONS public void FastThermalErosion(int iterations, float minSlope, float blendAmount) { erosionTypeInt = 0; erosionType = ErosionType.Thermal; thermalIterations = iterations; thermalMinSlope = minSlope; thermalFalloff = blendAmount; neighbourhood = Neighbourhood.Moore; ErosionProgressDelegate erosionProgressDelegate = new ErosionProgressDelegate(dummyErosionProgress); erodeAllTerrain(erosionProgressDelegate); }
public void FastHydraulicErosion(int iterations, float maxSlope, float blendAmount) { erosionTypeInt = 1; erosionType = ErosionType.Hydraulic; hydraulicTypeInt = 0; hydraulicType = HydraulicType.Fast; hydraulicIterations = iterations; hydraulicMaxSlope = maxSlope; hydraulicFalloff = blendAmount; neighbourhood = Neighbourhood.Moore; ErosionProgressDelegate erosionProgressDelegate = new ErosionProgressDelegate(dummyErosionProgress); erodeAllTerrain(erosionProgressDelegate); }
public void erodeAllTerrain(ErosionProgressDelegate erosionProgressDelegate) { erosionMode = ErosionMode.Filter; // Check enum vars... convertIntVarsToEnums(); // Error checking... Terrain ter = (Terrain) GetComponent(typeof(Terrain)); if (ter == null) { return; } try { TerrainData terData = ter.terrainData; int Tw = terData.heightmapWidth; int Th = terData.heightmapHeight; float[,] heightMap = terData.GetHeights(0, 0, Tw, Th); // Set the number of iterations and pass the height array to the appropriate erosion script... int iterations; switch (erosionType) { case ErosionType.Thermal: iterations = thermalIterations; heightMap = fastErosion(heightMap, new Vector2(Tw, Th), iterations, erosionProgressDelegate); break; case ErosionType.Hydraulic: iterations = hydraulicIterations; switch (hydraulicType) { case HydraulicType.Fast: heightMap = fastErosion(heightMap, new Vector2(Tw, Th), iterations, erosionProgressDelegate); break; case HydraulicType.Full: heightMap = fullHydraulicErosion(heightMap, new Vector2(Tw, Th), iterations, erosionProgressDelegate); break; case HydraulicType.Velocity: heightMap = velocityHydraulicErosion(heightMap, new Vector2(Tw, Th), iterations, erosionProgressDelegate); break; } break; case ErosionType.Tidal: Vector3 terSize = terData.size; if (tidalSeaLevel >= transform.position.y && tidalSeaLevel <= transform.position.y + terSize.y) { iterations = tidalIterations; heightMap = fastErosion(heightMap, new Vector2(Tw, Th), iterations, erosionProgressDelegate); } else { Debug.LogError("Sea level does not intersect terrain object. Erosion operation failed."); } break; case ErosionType.Wind: iterations = windIterations; heightMap = windErosion(heightMap, new Vector2(Tw, Th), iterations, erosionProgressDelegate); break; default: return; } // Apply it to the terrain object terData.SetHeights(0, 0, heightMap); } catch (Exception e) { Debug.LogError("An error occurred: "+e); } }
// -------------------------------------------------------------------------------------------------------- WIND EROSION private float[,] windErosion(float[,] heightMap, Vector2 arraySize, int iterations, ErosionProgressDelegate erosionProgressDelegate) { Terrain ter = (Terrain) GetComponent(typeof(Terrain)); TerrainData terData = ter.terrainData; Quaternion windQuaternion = Quaternion.Euler(0, (windDirection + 180.0f), 0); Vector3 windVector = windQuaternion * Vector3.forward; int Tw = (int) arraySize.x; int Th = (int) arraySize.y; float[,] stressMap = new float[Tw, Th]; float[,] flowMap = new float[Tw, Th]; float[,] velocityMap = new float[Tw, Th]; float[,] velocityDiffMap = new float[Tw, Th]; float[,] suspensionMap = new float[Tw, Th]; float[,] suspensionDiffMap = new float[Tw, Th]; float[,] heightDiffMap = new float[Tw, Th]; int xNeighbours; int yNeighbours; int xShift; int yShift; int xIndex; int yIndex; int Mx; int My; float materialAtIndex; float velocityAtCell; // Zero maps... for (My = 0; My < Th; My++) { for (Mx = 0; Mx < Tw; Mx++) { stressMap[Mx, My] = 0.0f; flowMap[Mx, My] = 0.0f; velocityMap[Mx, My] = 0.0f; velocityDiffMap[Mx, My] = 0.0f; suspensionMap[Mx, My] = 0.0f; suspensionDiffMap[Mx, My] = 0.0f; heightDiffMap[Mx, My] = 0.0f; } } // Start iterations... for (int iter = 0; iter < iterations; iter++) { // Drop material... for (My = 0; My < Th; My++) { for (Mx = 0; Mx < Tw; Mx++) { velocityAtCell = velocityMap[Mx, My]; float heightAtCell = heightMap[Mx, My]; float materialAtCell = suspensionMap[Mx, My]; float droppedMaterial = materialAtCell * windGravity; // * 1.0f - (velocityAtCell) suspensionMap[Mx, My] = materialAtCell - droppedMaterial; heightMap[Mx, My] = heightAtCell + droppedMaterial; } } // Calculate stress and flow... for (My = 0; My < Th; My++) { for (Mx = 0; Mx < Tw; Mx++) { float heightAtIndex = heightMap[Mx, My]; // ALTITUDE Vector3 pNormal = terData.GetInterpolatedNormal((float) Mx / Tw, (float) My / Th); // NORMAL float stress = (Vector3.Angle(pNormal, windVector) - 90) / 90; if (stress < 0.0f) { stress = 0.0f; } stressMap[Mx, My] = stress * heightAtIndex; float flow = 1.0f - Mathf.Abs(Vector3.Angle(pNormal, windVector) - 90) / 90; flowMap[Mx, My] = flow * heightAtIndex; // ^2 float inFlow = flow * heightAtIndex * windForce; // ^2 float velocityAtPoint = velocityMap[Mx, My]; float newVelocityAtPoint = velocityAtPoint + inFlow; // * UnityEngine.Random.value; velocityMap[Mx, My] = newVelocityAtPoint; // Lift material... materialAtIndex = suspensionMap[Mx, My]; float liftedMaterial = windLift * newVelocityAtPoint; // * UnityEngine.Random.value; if (materialAtIndex + liftedMaterial > windCapacity) { liftedMaterial = windCapacity - materialAtIndex; } suspensionMap[Mx, My] = materialAtIndex + liftedMaterial; heightMap[Mx, My] = heightAtIndex - liftedMaterial; } } // Calculate flow propagation... for (My = 0; My < Th; My++) { // y... if (My == 0) { yNeighbours = 2; yShift = 0; yIndex = 0; } else if (My == Th - 1) { yNeighbours = 2; yShift = -1; yIndex = 1; } else { yNeighbours = 3; yShift = -1; yIndex = 1; } for (Mx = 0; Mx < Tw; Mx++) { // x... if (Mx == 0) { xNeighbours = 2; xShift = 0; xIndex = 0; } else if (Mx == Tw - 1) { xNeighbours = 2; xShift = -1; xIndex = 1; } else { xNeighbours = 3; xShift = -1; xIndex = 1; } int Ny; int Nx; float flowAtIndex = flowMap[Mx, My]; float stressAtIndex = stressMap[Mx, My]; materialAtIndex = suspensionMap[Mx, My]; for (Ny = 0; Ny < yNeighbours; Ny++) { for (Nx = 0; Nx < xNeighbours; Nx++) { if (Nx != xIndex || Ny != yIndex) { if (neighbourhood == Neighbourhood.Moore || (neighbourhood == Neighbourhood.VonNeumann && (Nx == xIndex || Ny == yIndex))) { Vector3 vectorToNeighbour = new Vector3(Nx + xShift, 0, -1 * (Ny + yShift)); float pWeighting = (90 - Vector3.Angle(vectorToNeighbour, windVector)) / 90; if (pWeighting < 0.0f) { pWeighting = 0.0f; } // Propagate velocity... float propagatedVelocitySoFar = velocityDiffMap[Mx + Nx + xShift, My + Ny + yShift]; float propagatedVelocity = pWeighting * (flowAtIndex - stressAtIndex) * 0.1f; if (propagatedVelocity < 0.0f) { propagatedVelocity = 0.0f; } float propagatedVelocityAfter = propagatedVelocitySoFar + propagatedVelocity; // Pass to difference map... velocityDiffMap[Mx + Nx + xShift, My + Ny + yShift] = propagatedVelocityAfter; // Lose velocity... float lostVelocitySoFar = velocityDiffMap[Mx, My]; float lostVelocityAfter = lostVelocitySoFar - propagatedVelocity; // Pass to difference map... velocityDiffMap[Mx, My] = lostVelocityAfter; // Propagate material... float propagatedMaterialSoFar = suspensionDiffMap[Mx + Nx + xShift, My + Ny + yShift]; float propagatedMaterial = materialAtIndex * propagatedVelocity; float propagatedMaterialAfter = propagatedMaterialSoFar + propagatedMaterial; // Pass to difference map... suspensionDiffMap[Mx + Nx + xShift, My + Ny + yShift] = propagatedMaterialAfter; // Lose material... float lostMaterialSoFar = suspensionDiffMap[Mx, My]; float lostMaterialAfter = lostMaterialSoFar - propagatedMaterial; // Pass to difference map... suspensionDiffMap[Mx, My] = lostMaterialAfter; } } } } } } // Apply suspension difference map to suspension map... float suspensionAtCell; for (My = 0; My < Th; My++) { for (Mx = 0; Mx < Tw; Mx++) { suspensionAtCell = suspensionMap[Mx, My] + suspensionDiffMap[Mx, My]; if (suspensionAtCell > 1.0f) { suspensionAtCell = 1.0f; } else if (suspensionAtCell < 0.0f) { suspensionAtCell = 0.0f; } suspensionMap[Mx, My] = suspensionAtCell; suspensionDiffMap[Mx, My] = 0.0f; } } // Apply velocity difference map to velocity map... for (My = 0; My < Th; My++) { for (Mx = 0; Mx < Tw; Mx++) { velocityAtCell = velocityMap[Mx, My] + velocityDiffMap[Mx, My]; // Calculate entropy... velocityAtCell *= 1.0f - windEntropy; if (velocityAtCell > 1.0f) { velocityAtCell = 1.0f; } else if (velocityAtCell < 0.0f) { velocityAtCell = 0.0f; } velocityMap[Mx, My] = velocityAtCell; velocityDiffMap[Mx, My] = 0.0f; } } // Smooth... smoothIterations = 1; smoothBlend = 0.25f; float[,] smoothedHeightMap = (float[,]) heightMap.Clone(); GeneratorProgressDelegate generatorProgressDelegate = new GeneratorProgressDelegate(dummyGeneratorProgress); smoothedHeightMap = smooth(smoothedHeightMap, arraySize, generatorProgressDelegate); // Combine... for (My = 0; My < Th; My++) { for (Mx = 0; Mx < Tw; Mx++) { float oldHeightAtPoint = heightMap[Mx, My]; float newHeightAtPoint = smoothedHeightMap[Mx, My]; float blendAmount = stressMap[Mx, My] * windSmoothing; float blendedHeightAtPoint = (newHeightAtPoint * blendAmount) + (oldHeightAtPoint * (1.0f - blendAmount)); heightMap[Mx, My] = blendedHeightAtPoint; } } // Update progress... float percentComplete = (float) iter / (float) iterations; erosionProgressDelegate("Applying Wind Erosion", "Applying wind erosion.", iter, iterations, percentComplete); } return heightMap; }
// -------------------------------------------------------------------------------------------------------- HYDRAULIC (VELOCITY) EROSION private float[,] velocityHydraulicErosion(float[,] heightMap, Vector2 arraySize, int iterations, ErosionProgressDelegate erosionProgressDelegate) { int Tx = (int) arraySize.x; int Ty = (int) arraySize.y; float[,] precipitationMap = new float[Tx, Ty]; float[,] slopeMap = new float[Tx, Ty]; float[,] waterMap = new float[Tx, Ty]; float[,] waterDiffMap = new float[Tx, Ty]; float[,] velocityMap = new float[Tx, Ty]; float[,] velocityDiffMap = new float[Tx, Ty]; float[,] sedimentMap = new float[Tx, Ty]; float[,] sedimentDiffMap = new float[Tx, Ty]; int Mx; int My; int xNeighbours; int yNeighbours; int xShift; int yShift; int xIndex; int yIndex; float maxSediment; // Zero maps... for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { waterMap[Mx, My] = 0.0f; waterDiffMap[Mx, My] = 0.0f; velocityMap[Mx, My] = 0.0f; velocityDiffMap[Mx, My] = 0.0f; sedimentMap[Mx, My] = 0.0f; sedimentDiffMap[Mx, My] = 0.0f; } } // Cache precipitation map... for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { float precipitationAmount = heightMap[Mx, My]; precipitationMap[Mx, My] = precipitationAmount; } } // Cache slope map and initialise velocity map... float tCumulative; int Nx; int Ny; float t; float heightAtIndex; float heightAtPoint; int nNeighbours; for (My = 0; My < Ty; My++) { // y... if (My == 0) { yNeighbours = 2; yShift = 0; yIndex = 0; } else if (My == Ty - 1) { yNeighbours = 2; yShift = -1; yIndex = 1; } else { yNeighbours = 3; yShift = -1; yIndex = 1; } for (Mx = 0; Mx < Tx; Mx++) { // x... if (Mx == 0) { xNeighbours = 2; xShift = 0; xIndex = 0; } else if (Mx == Tx - 1) { xNeighbours = 2; xShift = -1; xIndex = 1; } else { xNeighbours = 3; xShift = -1; xIndex = 1; } // Calculate slope and create velocity map... tCumulative = 0.0f; heightAtIndex = heightMap[Mx + xIndex + xShift, My + yIndex + yShift]; // Get height at index nNeighbours = 0; for (Ny = 0; Ny < yNeighbours; Ny++) { for (Nx = 0; Nx < xNeighbours; Nx++) { if (Nx != xIndex || Ny != yIndex) { if (neighbourhood == Neighbourhood.Moore || (neighbourhood == Neighbourhood.VonNeumann && (Nx == xIndex || Ny == yIndex))) { heightAtPoint = heightMap[Mx + Nx + xShift, My + Ny + yShift]; // Get height at point t = Mathf.Abs(heightAtIndex - heightAtPoint); tCumulative += t; nNeighbours++; } } } } float tAverage = tCumulative / nNeighbours; slopeMap[Mx + xIndex + xShift, My + yIndex + yShift] = tAverage; // velocityMap[Mx + xIndex + xShift, My + yIndex + yShift] = Mathf.Min(tAverage * hydraulicVelocity, 1.0f); } } // Start iterations... for (int iter = 0; iter < iterations; iter++) { // Add water proportional to precipitation... for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { float waterAmount = waterMap[Mx, My] + precipitationMap[Mx, My] * hydraulicVelocityRainfall; if (waterAmount > 1.0f) { waterAmount = 1.0f; } waterMap[Mx, My] = waterAmount; } } // Dissolve material as sediment... for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { float sedimentAmount = sedimentMap[Mx, My]; maxSediment = waterMap[Mx, My] * hydraulicVelocitySedimentSaturation; if (sedimentAmount < maxSediment) { float erodedSediment = waterMap[Mx, My] * velocityMap[Mx, My] * hydraulicVelocitySedimentSolubility; if (sedimentAmount + erodedSediment > maxSediment) { erodedSediment = maxSediment - sedimentAmount; } heightAtIndex = heightMap[Mx, My]; if (erodedSediment > heightAtIndex) { erodedSediment = heightAtIndex; } sedimentMap[Mx, My] = sedimentAmount + erodedSediment; heightMap[Mx, My] = heightAtIndex - erodedSediment; } } } // Calculate velocity and move water... for (My = 0; My < Ty; My++) { // y... if (My == 0) { yNeighbours = 2; yShift = 0; yIndex = 0; } else if (My == Ty - 1) { yNeighbours = 2; yShift = -1; yIndex = 1; } else { yNeighbours = 3; yShift = -1; yIndex = 1; } for (Mx = 0; Mx < Tx; Mx++) { // x... if (Mx == 0) { xNeighbours = 2; xShift = 0; xIndex = 0; } else if (Mx == Tx - 1) { xNeighbours = 2; xShift = -1; xIndex = 1; } else { xNeighbours = 3; xShift = -1; xIndex = 1; } // Calculate slope... tCumulative = 0.0f; heightAtIndex = heightMap[Mx, My]; // Get height at index float hCumulative = heightAtIndex; float waterAtIndex = waterMap[Mx, My]; // Get water at index nNeighbours = 0; float waterAtPoint; for (Ny = 0; Ny < yNeighbours; Ny++) { for (Nx = 0; Nx < xNeighbours; Nx++) { if (Nx != xIndex || Ny != yIndex) { if (neighbourhood == Neighbourhood.Moore || (neighbourhood == Neighbourhood.VonNeumann && (Nx == xIndex || Ny == yIndex))) { heightAtPoint = heightMap[Mx + Nx + xShift, My + Ny + yShift]; // Get height at point waterAtPoint = waterMap[Mx + Nx + xShift, My + Ny + yShift]; // Get water at point t = (heightAtIndex + waterAtIndex) - (heightAtPoint + waterAtPoint); // Only calculate downhill cells... if (t > 0) { tCumulative += t; hCumulative += heightAtIndex + waterAtIndex; nNeighbours++; } } } } } float velocityAtIndex = velocityMap[Mx, My]; float slopeAtIndex = slopeMap[Mx, My]; float sedimentAtIndex = sedimentMap[Mx, My]; float totalVelocityAtIndex = velocityAtIndex + (hydraulicVelocity * slopeAtIndex); // Calculate water to be transported away from the index... float hAverage = hCumulative / (nNeighbours + 1); float dWater = (heightAtIndex + waterAtIndex) - hAverage; float transportedWater = Mathf.Min(waterAtIndex, dWater * (1.0f + velocityAtIndex)); float waterDiffAtIndexSoFar = waterDiffMap[Mx, My]; // Pass to difference map... float waterAtIndexDiff = waterDiffAtIndexSoFar - transportedWater; waterDiffMap[Mx, My] = waterAtIndexDiff; float transferredVelocity = totalVelocityAtIndex * (transportedWater / waterAtIndex); float transferredSediment = sedimentAtIndex * (transportedWater / waterAtIndex); // Neighbours... for (Ny = 0; Ny < yNeighbours; Ny++) { for (Nx = 0; Nx < xNeighbours; Nx++) { if (Nx != xIndex || Ny != yIndex) { if (neighbourhood == Neighbourhood.Moore || (neighbourhood == Neighbourhood.VonNeumann && (Nx == xIndex || Ny == yIndex))) { heightAtPoint = heightMap[Mx + Nx + xShift, My + Ny + yShift]; // Get height at point waterAtPoint = waterMap[Mx + Nx + xShift, My + Ny + yShift]; // Get water at point t = (heightAtIndex + waterAtIndex) - (heightAtPoint + waterAtPoint); // Only affect downhill cells... if (t > 0.0f) { // Move water... float transportedWaterSoFar = waterDiffMap[Mx + Nx + xShift, My + Ny + yShift]; float transportedWaterProportional = transportedWater * (t / tCumulative); float transportedWaterAfter = transportedWaterSoFar + transportedWaterProportional; // Pass to difference map... waterDiffMap[Mx + Nx + xShift, My + Ny + yShift] = transportedWaterAfter; // Transfer velocity... float transferredVelocitySoFar = velocityDiffMap[Mx + Nx + xShift, My + Ny + yShift]; float transferredVelocityProportional = transferredVelocity * hydraulicMomentum * (t / tCumulative); float transferredVelocityAfter = transferredVelocitySoFar + transferredVelocityProportional; // Pass to difference map... velocityDiffMap[Mx + Nx + xShift, My + Ny + yShift] = transferredVelocityAfter; // Transfer sediment... float transferredSedimentSoFar = sedimentDiffMap[Mx + Nx + xShift, My + Ny + yShift]; float transferredSedimentProportional = transferredSediment * hydraulicMomentum * (t / tCumulative); float transferredSedimentAfter = transferredSedimentSoFar + transferredSedimentProportional; // Pass to difference map... sedimentDiffMap[Mx + Nx + xShift, My + Ny + yShift] = transferredSedimentAfter; } } } } } // Lose velocity at index... float velocityDiffSoFar = velocityDiffMap[Mx, My]; velocityDiffMap[Mx, My] = velocityDiffSoFar - transferredVelocity; } } // Apply velocity difference map to velocity map... float velocityAtCell; for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { velocityAtCell = velocityMap[Mx, My] + velocityDiffMap[Mx, My]; // Calculate entropy... velocityAtCell *= 1.0f - hydraulicEntropy; if (velocityAtCell > 1.0f) { velocityAtCell = 1.0f; } else if (velocityAtCell < 0.0f) { velocityAtCell = 0.0f; } velocityMap[Mx, My] = velocityAtCell; velocityDiffMap[Mx, My] = 0.0f; } } // Apply water difference map to water map... float waterAtCell; for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { waterAtCell = waterMap[Mx, My] + waterDiffMap[Mx, My]; // Calculate evaporation... float evaporatedWater = waterAtCell * hydraulicVelocityEvaporation; waterAtCell -= evaporatedWater; if (waterAtCell > 1.0f) { waterAtCell = 1.0f; } else if (waterAtCell < 0.0f) { waterAtCell = 0.0f; } waterMap[Mx, My] = waterAtCell; waterDiffMap[Mx, My] = 0.0f; } } // Apply sediment difference map to sediment map... float sedimentAtCell; for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { sedimentAtCell = sedimentMap[Mx, My] + sedimentDiffMap[Mx, My]; if (sedimentAtCell > 1.0f) { sedimentAtCell = 1.0f; } else if (sedimentAtCell < 0.0f) { sedimentAtCell = 0.0f; } sedimentMap[Mx, My] = sedimentAtCell; sedimentDiffMap[Mx, My] = 0.0f; } } // Deposit sediment... float heightAtCell; for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { maxSediment = waterMap[Mx, My] * hydraulicVelocitySedimentSaturation; sedimentAtCell = sedimentMap[Mx, My]; if (sedimentAtCell > maxSediment) { float depositedSediment = sedimentAtCell - maxSediment; sedimentMap[Mx, My] = maxSediment; heightAtCell = heightMap[Mx, My]; heightMap[Mx, My] = heightAtCell + depositedSediment; } } } // Downcutting... for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { velocityAtCell = waterMap[Mx, My]; heightAtCell = heightMap[Mx, My]; float heightModifier = 1 - (Mathf.Abs(0.5f - heightAtCell) * 2); float cutMaterial = hydraulicDowncutting * velocityAtCell * heightModifier; heightAtCell -= cutMaterial; heightMap[Mx, My] = heightAtCell; } } // Update progress... float percentComplete = (float) iter / (float) iterations; erosionProgressDelegate("Applying Hydraulic Erosion", "Applying hydraulic erosion.", iter, iterations, percentComplete); } return heightMap; }
// -------------------------------------------------------------------------------------------------------- HYDRAULIC (FULL) EROSION private float[,] fullHydraulicErosion(float[,] heightMap, Vector2 arraySize, int iterations, ErosionProgressDelegate erosionProgressDelegate) { int Tx = (int) arraySize.x; int Ty = (int) arraySize.y; float[,] waterMap = new float[Tx, Ty]; float[,] waterDiffMap = new float[Tx, Ty]; float[,] sedimentMap = new float[Tx, Ty]; float[,] sedimentDiffMap = new float[Tx, Ty]; int Mx; int My; int xNeighbours; int yNeighbours; int xShift; int yShift; int xIndex; int yIndex; int Nx; int Ny; float t; float heightAtIndex; float heightAtPoint; int nNeighbours; float maxSediment; float sedimentAtCell; float heightAtCell; // Zero maps... for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { waterMap[Mx, My] = 0.0f; waterDiffMap[Mx, My] = 0.0f; sedimentMap[Mx, My] = 0.0f; sedimentDiffMap[Mx, My] = 0.0f; } } // Start iterations... for (int iter = 0; iter < iterations; iter++) { // Add water proportional to precipitation... for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { float waterAmount = waterMap[Mx, My] + hydraulicRainfall; if (waterAmount > 1.0f) { waterAmount = 1.0f; } waterMap[Mx, My] = waterAmount; } } // Dissolve material as sediment... for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { float sedimentAmount = sedimentMap[Mx, My]; maxSediment = waterMap[Mx, My] * hydraulicSedimentSaturation; if (sedimentAmount < maxSediment) { float erodedSediment = waterMap[Mx, My] * hydraulicSedimentSolubility; if (sedimentAmount + erodedSediment > maxSediment) { erodedSediment = maxSediment - sedimentAmount; } heightAtIndex = heightMap[Mx, My]; if (erodedSediment > heightAtIndex) { erodedSediment = heightAtIndex; } sedimentMap[Mx, My] = sedimentAmount + erodedSediment; heightMap[Mx, My] = heightAtIndex - erodedSediment; } } } // Find slope and move water... for (My = 0; My < Ty; My++) { // y... if (My == 0) { yNeighbours = 2; yShift = 0; yIndex = 0; } else if (My == Ty - 1) { yNeighbours = 2; yShift = -1; yIndex = 1; } else { yNeighbours = 3; yShift = -1; yIndex = 1; } for (Mx = 0; Mx < Tx; Mx++) { // x... if (Mx == 0) { xNeighbours = 2; xShift = 0; xIndex = 0; } else if (Mx == Tx - 1) { xNeighbours = 2; xShift = -1; xIndex = 1; } else { xNeighbours = 3; xShift = -1; xIndex = 1; } // Calculate slope... float tCumulative = 0.0f; float tMax = 0.0f; heightAtIndex = heightMap[Mx + xIndex + xShift, My + yIndex + yShift]; // Get height at index float waterAtIndex = waterMap[Mx + xIndex + xShift, My + yIndex + yShift]; // Get water at index float waterAtPoint; float hCumulative = heightAtIndex; nNeighbours = 0; for (Ny = 0; Ny < yNeighbours; Ny++) { for (Nx = 0; Nx < xNeighbours; Nx++) { if (Nx != xIndex || Ny != yIndex) { if (neighbourhood == Neighbourhood.Moore || (neighbourhood == Neighbourhood.VonNeumann && (Nx == xIndex || Ny == yIndex))) { heightAtPoint = heightMap[Mx + Nx + xShift, My + Ny + yShift]; // Get height at point waterAtPoint = waterMap[Mx + Nx + xShift, My + Ny + yShift]; // Get water at point t = (heightAtIndex + waterAtIndex) - (heightAtPoint + waterAtPoint); if (t > 0) { tCumulative += t; hCumulative += heightAtPoint + waterAtPoint; nNeighbours++; if (t > tMax) { t = tMax; } } } } } } // Calculate water to be transported away from the index... float hAverage = hCumulative / (nNeighbours + 1); float dWater = (heightAtIndex + waterAtIndex) - hAverage; float transportedWater = Mathf.Min(waterAtIndex, dWater); float waterDiffAtIndexSoFar = waterDiffMap[Mx + xIndex + xShift, My + yIndex + yShift]; float waterAtIndexDiff = waterDiffAtIndexSoFar - transportedWater; // Pass to difference map... waterDiffMap[Mx + xIndex + xShift, My + yIndex + yShift] = waterAtIndexDiff; // Calculate sediment to be transported away from the index... float sedimentAtIndex = sedimentMap[Mx + xIndex + xShift, My + yIndex + yShift]; float transportedSediment = sedimentAtIndex * (transportedWater / waterAtIndex); float sedimentDiffAtIndexSoFar = sedimentDiffMap[Mx + xIndex + xShift, My + yIndex + yShift]; float sedimentAtIndexDiff = sedimentDiffAtIndexSoFar - transportedSediment; // Pass to difference map... sedimentDiffMap[Mx + xIndex + xShift, My + yIndex + yShift] = sedimentAtIndexDiff; // Neighbours... for (Ny = 0; Ny < yNeighbours; Ny++) { for (Nx = 0; Nx < xNeighbours; Nx++) { if (Nx != xIndex || Ny != yIndex) { if (neighbourhood == Neighbourhood.Moore || (neighbourhood == Neighbourhood.VonNeumann && (Nx == xIndex || Ny == yIndex))) { heightAtPoint = heightMap[Mx + Nx + xShift, My + Ny + yShift]; // Get height at point waterAtPoint = waterMap[Mx + Nx + xShift, My + Ny + yShift]; // Get water at point t = (heightAtIndex + waterAtIndex) - (heightAtPoint + waterAtPoint); // Only affect downhill cells... if (t > 0) { // Move water... float transportedWaterSoFar = waterDiffMap[Mx + Nx + xShift, My + Ny + yShift]; float transportedWaterProportional = transportedWater * (t / tCumulative); float transportedWaterAfter = transportedWaterSoFar + transportedWaterProportional; // Pass to difference map... waterDiffMap[Mx + Nx + xShift, My + Ny + yShift] = transportedWaterAfter; // Move sediment... float transportedSedimentSoFar = sedimentDiffMap[Mx + Nx + xShift, My + Ny + yShift]; float transportedSedimentProportional = transportedSediment * (t / tCumulative); float transportedSedimentAfter = transportedSedimentSoFar + transportedSedimentProportional; // Pass to difference map... sedimentDiffMap[Mx + Nx + xShift, My + Ny + yShift] = transportedSedimentAfter; } } } } } } } // Apply water difference map to water map... float waterAtCell; for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { waterAtCell = waterMap[Mx, My] + waterDiffMap[Mx, My]; // Calculate evaporation... float evaporatedWater = waterAtCell * hydraulicEvaporation; waterAtCell -= evaporatedWater; if (waterAtCell < 0.0f) { waterAtCell = 0.0f; } waterMap[Mx, My] = waterAtCell; waterDiffMap[Mx, My] = 0.0f; } } // Apply sediment difference map to sediment map... for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { sedimentAtCell = sedimentMap[Mx, My] + sedimentDiffMap[Mx, My]; if (sedimentAtCell > 1.0f) { sedimentAtCell = 1.0f; } else if (sedimentAtCell < 0.0f) { sedimentAtCell = 0.0f; } sedimentMap[Mx, My] = sedimentAtCell; sedimentDiffMap[Mx, My] = 0.0f; } } // Deposit sediment... for (My = 0; My < Ty; My++) { for (Mx = 0; Mx < Tx; Mx++) { maxSediment = waterMap[Mx, My] * hydraulicSedimentSaturation; sedimentAtCell = sedimentMap[Mx, My]; if (sedimentAtCell > maxSediment) { float depositedSediment = sedimentAtCell - maxSediment; sedimentMap[Mx, My] = maxSediment; heightAtCell = heightMap[Mx, My]; heightMap[Mx, My] = heightAtCell + depositedSediment; } } } // Update progress... float percentComplete = (float) iter / (float) iterations; erosionProgressDelegate("Applying Hydraulic Erosion", "Applying hydraulic erosion.", iter, iterations, percentComplete); } return heightMap; }
private float[,] fastErosion(float[,] heightMap, Vector2 arraySize, int iterations, ErosionProgressDelegate erosionProgressDelegate) { int Tw = (int) arraySize.y; int Th = (int) arraySize.x; float[,] heightDiffMap = new float[Tw, Th]; Terrain ter = (Terrain) GetComponent(typeof(Terrain)); TerrainData terData = ter.terrainData; Vector3 terSize = terData.size; float minSlopeBlendMin = 0.0f; float minSlopeBlendMax = 0.0f; float maxSlopeBlendMin = 0.0f; float maxSlopeBlendMax = 0.0f; float seaLevel = 0.0f; float lowerTidalLimit = 0.0f; float upperTidalLimit = 0.0f; float tidalRange = 0.0f; float cliffLimit = 0.0f; switch (erosionType) { case ErosionType.Thermal: minSlopeBlendMin = ((terSize.x / Tw) * Mathf.Tan(thermalMinSlope * Mathf.Deg2Rad)) / terSize.y; if (minSlopeBlendMin > 1.0f) { minSlopeBlendMin = 1.0f; } if (thermalFalloff == 1.0f) { thermalFalloff = 0.999f; } float thermalMaxSlope = thermalMinSlope + ((90 - thermalMinSlope) * thermalFalloff); minSlopeBlendMax = ((terSize.x / Tw) * Mathf.Tan(thermalMaxSlope * Mathf.Deg2Rad)) / terSize.y; if (minSlopeBlendMax > 1.0f) { minSlopeBlendMax = 1.0f; } break; case ErosionType.Hydraulic: maxSlopeBlendMax = ((terSize.x / Tw) * Mathf.Tan(hydraulicMaxSlope * Mathf.Deg2Rad)) / terSize.y; if (hydraulicFalloff == 0.0f) { hydraulicFalloff = 0.001f; } float hydraulicMinSlope = hydraulicMaxSlope * (1 - hydraulicFalloff); maxSlopeBlendMin = ((terSize.x / Tw) * Mathf.Tan(hydraulicMinSlope * Mathf.Deg2Rad)) / terSize.y; break; case ErosionType.Tidal: seaLevel = (tidalSeaLevel - transform.position.y) / (transform.position.y + terSize.y); lowerTidalLimit = (tidalSeaLevel - transform.position.y - tidalRangeAmount) / (transform.position.y + terSize.y); upperTidalLimit = (tidalSeaLevel - transform.position.y + tidalRangeAmount) / (transform.position.y + terSize.y); tidalRange = upperTidalLimit - seaLevel; cliffLimit = ((terSize.x / Tw) * Mathf.Tan(tidalCliffLimit * Mathf.Deg2Rad)) / terSize.y; break; default: return heightMap; } int xNeighbours; int yNeighbours; int xShift; int yShift; int xIndex; int yIndex; int Tx; int Ty; // Start iterations... for (int iter = 0; iter < iterations; iter++) { for (Ty = 0; Ty < Th; Ty++) { // y... if (Ty == 0) { yNeighbours = 2; yShift = 0; yIndex = 0; } else if (Ty == Th - 1) { yNeighbours = 2; yShift = -1; yIndex = 1; } else { yNeighbours = 3; yShift = -1; yIndex = 1; } for (Tx = 0; Tx < Tw; Tx++) { // x... if (Tx == 0) { xNeighbours = 2; xShift = 0; xIndex = 0; } else if (Tx == Tw - 1) { xNeighbours = 2; xShift = -1; xIndex = 1; } else { xNeighbours = 3; xShift = -1; xIndex = 1; } // Calculate slope... float tMin = 1.0f; float tMax = 0.0f; float tCumulative = 0.0f; int Ny; int Nx; float t; float heightAtIndex = heightMap[Tx + xIndex + xShift, Ty + yIndex + yShift]; // Get height at index float hCumulative = heightAtIndex; int nNeighbours = 0; for (Ny = 0; Ny < yNeighbours; Ny++) { for (Nx = 0; Nx < xNeighbours; Nx++) { if (Nx != xIndex || Ny != yIndex) { if (neighbourhood == Neighbourhood.Moore || (neighbourhood == Neighbourhood.VonNeumann && (Nx == xIndex || Ny == yIndex))) { float heightAtPoint = heightMap[Tx + Nx + xShift, Ty + Ny + yShift]; // Get height at point // Tidal... hCumulative += heightAtPoint; // Others... t = heightAtIndex - heightAtPoint; if (t > 0) { tCumulative += t; if (t < tMin) { tMin = t; } if (t > tMax) { tMax = t; } } nNeighbours++; } } } } float tAverage = tCumulative / nNeighbours; // float tAverage = tMax; // Erosion type... bool doErode = false; switch (erosionType) { case ErosionType.Thermal: if (tAverage >= minSlopeBlendMin) { doErode = true; } break; case ErosionType.Hydraulic: if (tAverage > 0 && tAverage <= maxSlopeBlendMax) { doErode = true; } break; case ErosionType.Tidal: if (tAverage > 0 && tAverage <= cliffLimit && heightAtIndex < upperTidalLimit && heightAtIndex > lowerTidalLimit) { doErode = true; } break; default: return heightMap; } if (doErode) { float blendAmount; if (erosionType == ErosionType.Tidal) { // Tidal... float hAverage = hCumulative / (nNeighbours + 1); float dTidalSeaLevel = Mathf.Abs(seaLevel - heightAtIndex); blendAmount = dTidalSeaLevel / tidalRange; float blendHeight = heightAtIndex * blendAmount + hAverage * (1 - blendAmount); float blendTidalSeaLevel = Mathf.Pow(dTidalSeaLevel, 3); heightMap[Tx + xIndex + xShift, Ty + yIndex + yShift] = seaLevel * blendTidalSeaLevel + blendHeight * (1 - blendTidalSeaLevel); } else { // Thermal or Hydraulic... float blendRange; if (erosionType == ErosionType.Thermal) { if (tAverage > minSlopeBlendMax) { blendAmount = 1; } else { blendRange = minSlopeBlendMax - minSlopeBlendMin; blendAmount = (tAverage - minSlopeBlendMin) / blendRange; // minSlopeBlendMin = 0; minSlopeBlendMax = 1 } } else { if (tAverage < maxSlopeBlendMin) { blendAmount = 1; } else { blendRange = maxSlopeBlendMax - maxSlopeBlendMin; blendAmount = 1 - ((tAverage - maxSlopeBlendMin) / blendRange); // maxSlopeBlendMin = 1; maxSlopeBlendMax = 0 } } float m = tMin / 2 * blendAmount; float pointValue = heightMap[Tx + xIndex + xShift, Ty + yIndex + yShift]; if (erosionMode == ErosionMode.Filter || (erosionMode == ErosionMode.Brush && useDifferenceMaps)) { // Pass to difference map... float heightDiffAtIndexSoFar = heightDiffMap[Tx + xIndex + xShift, Ty + yIndex + yShift]; float heightAtIndexDiff = heightDiffAtIndexSoFar - m; heightDiffMap[Tx + xIndex + xShift, Ty + yIndex + yShift] = heightAtIndexDiff; } else { float pointValueAfter = pointValue - m; if (pointValueAfter < 0) { pointValueAfter = 0; } heightMap[Tx + xIndex + xShift, Ty + yIndex + yShift] = pointValueAfter; } for (Ny = 0; Ny < yNeighbours; Ny++) { for (Nx = 0; Nx < xNeighbours; Nx++) { if (Nx != xIndex || Ny != yIndex) { if (neighbourhood == Neighbourhood.Moore || (neighbourhood == Neighbourhood.VonNeumann && (Nx == xIndex || Ny == yIndex))) { float neighbourValue = heightMap[Tx + Nx + xShift, Ty + Ny + yShift]; t = pointValue - neighbourValue; // Only move material downhill... if (t > 0) { float mProportional = m * (t / tCumulative); if (erosionMode == ErosionMode.Filter || (erosionMode == ErosionMode.Brush && useDifferenceMaps)) { // Pass to difference map... float heightDiffAtNeighbourSoFar = heightDiffMap[Tx + Nx + xShift, Ty + Ny + yShift]; float heightAtNeighbourDiff = heightDiffAtNeighbourSoFar + mProportional; heightDiffMap[Tx + Nx + xShift, Ty + Ny + yShift] = heightAtNeighbourDiff; } else { neighbourValue += mProportional; if (neighbourValue < 0) { neighbourValue = 0; } heightMap[Tx + Nx + xShift, Ty + Ny + yShift] = neighbourValue; } } } } } } } } } } if ((erosionMode == ErosionMode.Filter || (erosionMode == ErosionMode.Brush && useDifferenceMaps)) && erosionType != ErosionType.Tidal) { // Apply height difference map to height map... float heightAtCell; for (Ty = 0; Ty < Th; Ty++) { for (Tx = 0; Tx < Tw; Tx++) { heightAtCell = heightMap[Tx, Ty] + heightDiffMap[Tx, Ty]; if (heightAtCell > 1.0f) { heightAtCell = 1.0f; } else if (heightAtCell < 0.0f) { heightAtCell = 0.0f; } heightMap[Tx, Ty] = heightAtCell; heightDiffMap[Tx, Ty] = 0.0f; } } } if (erosionMode == ErosionMode.Filter) { // Update progress... string titleString = ""; string displayString = ""; switch (erosionType) { case ErosionType.Thermal: titleString = "Applying Thermal Erosion"; displayString = "Applying thermal erosion."; break; case ErosionType.Hydraulic: titleString = "Applying Hydraulic Erosion"; displayString = "Applying hydraulic erosion."; break; case ErosionType.Tidal: titleString = "Applying Tidal Erosion"; displayString = "Applying tidal erosion."; break; default: return heightMap; } float percentComplete = (float) iter / (float) iterations; erosionProgressDelegate(titleString, displayString, iter, iterations, percentComplete); } } return heightMap; }
private void erodeTerrainWithBrush() { erosionMode = ErosionMode.Brush; // Error checking... Terrain ter = (Terrain) GetComponent(typeof(Terrain)); if (ter == null) { return; } int Px = 0; int Py = 0; try { TerrainData terData = ter.terrainData; int Tw = terData.heightmapWidth; int Th = terData.heightmapHeight; Vector3 Ts = terData.size; int Sx = (int) Mathf.Floor((Tw / Ts.x) * brushSize); int Sy = (int) Mathf.Floor((Th / Ts.z) * brushSize); Vector3 localBrushPosition = transform.InverseTransformPoint(brushPosition); Px = (int) Mathf.Round((localBrushPosition.x / Ts.x) * Tw - (Sx / 2)); Py = (int) Mathf.Round((localBrushPosition.z / Ts.z) * Th - (Sy / 2)); if (Px < 0) { Sx = Sx + Px; Px = 0; } if (Py < 0) { Sy = Sy + Py; Py = 0; } if (Px + Sx > Tw) { Sx = Tw - Px; } if (Py + Sy > Th) { Sy = Th - Py; } float[,] heightMap = terData.GetHeights(Px, Py, Sx, Sy); Sx = heightMap.GetLength(1); Sy = heightMap.GetLength(0); // Pass the height array to the erosion script... float[,] erodedheightMap = (float[,]) heightMap.Clone(); ErosionProgressDelegate erosionProgressDelegate = new ErosionProgressDelegate(dummyErosionProgress); erodedheightMap = fastErosion(erodedheightMap, new Vector2(Sx, Sy), 1, erosionProgressDelegate); // Apply it to the terrain object float sampleRadius = Sx / 2.0f; for (int Ty = 0; Ty < Sx; Ty++) { for (int Tx = 0; Tx < Sy; Tx++) { float oldHeightAtPoint = heightMap[Tx, Ty]; float newHeightAtPoint = erodedheightMap[Tx, Ty]; float distancefromOrigin = Vector2.Distance(new Vector2(Tx, Ty), new Vector2(sampleRadius, sampleRadius)); float weightAtPoint = 1.0f - ((distancefromOrigin - (sampleRadius - (sampleRadius * brushSoftness))) / (sampleRadius * brushSoftness)); if (weightAtPoint < 0.0f) { weightAtPoint = 0.0f; } else if (weightAtPoint > 1.0f) { weightAtPoint = 1.0f; } weightAtPoint *= brushOpacity; float blendedHeightAtPoint = (newHeightAtPoint * weightAtPoint) + (oldHeightAtPoint * (1.0f - weightAtPoint)); heightMap[Tx, Ty] = blendedHeightAtPoint; } } terData.SetHeights(Px, Py, heightMap); } catch (Exception e) { Debug.LogError("A brush error occurred: "+e); } }
public void WindErosion(int iterations, float direction, float force, float lift, float gravity, float capacity, float entropy, float smoothing) { erosionTypeInt = 3; erosionType = ErosionType.Wind; windIterations = iterations; windDirection = direction; windForce = force; windLift = lift; windGravity = gravity; windCapacity = capacity; windEntropy = entropy; windSmoothing = smoothing; neighbourhood = Neighbourhood.Moore; ErosionProgressDelegate erosionProgressDelegate = new ErosionProgressDelegate(dummyErosionProgress); erodeAllTerrain(erosionProgressDelegate); }
public void VelocityHydraulicErosion(int iterations, float rainfall, float evaporation, float solubility, float saturation, float velocity, float momentum, float entropy, float downcutting) { erosionTypeInt = 1; erosionType = ErosionType.Hydraulic; hydraulicTypeInt = 2; hydraulicType = HydraulicType.Velocity; hydraulicIterations = iterations; hydraulicVelocityRainfall = rainfall; hydraulicVelocityEvaporation = evaporation; hydraulicVelocitySedimentSolubility = solubility; hydraulicVelocitySedimentSaturation = saturation; hydraulicVelocity = velocity; hydraulicMomentum = momentum; hydraulicEntropy = entropy; hydraulicDowncutting = downcutting; neighbourhood = Neighbourhood.Moore; ErosionProgressDelegate erosionProgressDelegate = new ErosionProgressDelegate(dummyErosionProgress); erodeAllTerrain(erosionProgressDelegate); }
public void TidalErosion(int iterations, float seaLevel, float tidalRange, float cliffLimit) { erosionTypeInt = 2; erosionType = ErosionType.Tidal; tidalIterations = iterations; tidalSeaLevel = seaLevel; tidalRangeAmount = tidalRange; tidalCliffLimit = cliffLimit; neighbourhood = Neighbourhood.Moore; ErosionProgressDelegate erosionProgressDelegate = new ErosionProgressDelegate(dummyErosionProgress); erodeAllTerrain(erosionProgressDelegate); }
void waterErosion(int iterations, ErosionProgressDelegate erosionProgressDelegate) { for (int iter = 0; iter < iterations; iter++) { float percentComplete = (float)iter / (float)iterations; erosionProgressDelegate ("Applying Water Erosion", "Applying water erosion.", iter, iterations, percentComplete); for (int y = 0; y < Ty; y++) { for (int x = 0; x < Tx; x++) { waterMap [x, y] = waterErosionRainfall; sedimentMap [x, y] = 0.0f; } } int waterCounter = 100; do { waterExists = false; for (int y = 0; y < Ty; y++) { for (int x = 0; x < Tx; x++) { moveWater (x, y); } } waterCounter--; } while (waterExists && (waterCounter > 0)); for (int y = 0; y < Ty; y++) { for (int x = 0; x < Tx; x++) { addSediment (x, y); } } } }