public void Erode(SimpleHeightArray inHeightArray, ThermalErosionConfiguration configuration) { var tParam = configuration.TParam; var cParam = configuration.CParam; var stepCount = configuration.StepCount; var thermalErosionGroundMover = configuration.GroundMover; var neighbourChooser = configuration.NeighboursChooser; var finalDifferenceArray = new SimpleHeightArray(inHeightArray.Width, inHeightArray.Height); for (int stepIndex = 0; stepIndex < stepCount; stepIndex++) { Parallel.For <SimpleHeightArray>(0, inHeightArray.Height, new ParallelOptions { MaxDegreeOfParallelism = 4 }, () => new SimpleHeightArray(inHeightArray.Width, inHeightArray.Height), (y, loop, localDifferenceArray) => { // Parallel.For(0, inHeightArray.Height,new ParallelOptions { MaxDegreeOfParallelism = 3 }, y => // for (int y = 0; y < inHeightArray.Height; y++) // { for (int x = 0; x < inHeightArray.Width; x++) { var point = new IntVector2(x, y); var thisValue = inHeightArray.GetValue(point); var neighbours = configuration.NeighbourFinder.Find(inHeightArray, point) .Select(n => new NeighbourInfo(n, thisValue - inHeightArray.GetValue(n))).ToList(); neighbours = neighbours.Where(c => neighbourChooser.Choose(c, configuration)).ToList(); if (!neighbours.Any()) { continue; } var changeArray = localDifferenceArray; //changeArray = finalDifferenceArray; thermalErosionGroundMover.Move(neighbours, configuration, changeArray, point); } return(localDifferenceArray); }, (localDifferenceArray) => { finalDifferenceArray.SumValue(localDifferenceArray); }); for (int y = 0; y < inHeightArray.Height; y++) { for (int x = 0; x < inHeightArray.Width; x++) { var point = new IntVector2(x, y); inHeightArray.AddValue(point, finalDifferenceArray.GetValue(point)); finalDifferenceArray.SetValue(point, 0); } } } }
public TerrainErosionDebugOutput ErodeWithDebug(SimpleHeightArray heightMap, MeiHydraulicEroderConfiguration configuration) { var debugOutput = new TerrainErosionDebugOutput(); var sb = new StringBuilder(); int stepCount = configuration.StepCount; float deltaT = configuration.DeltaT; float constantWaterAdding = configuration.ConstantWaterAdding; float A_pipeCrossSection = configuration.A_PipeCrossSection; float l_pipeLength = configuration.L_PipeLength; float g_GravityAcceleration = configuration.GravityAcceleration; float ks_DissolvingConstant = configuration.DissolvingConstant; float kd_DepositionConstant = configuration.DepositionConstant; float ke_EvaporationConstant = configuration.EvaporationConstant; float kc_SedimentCapacityConstant = configuration.SedimentCapacityConstant; Vector2 gridSize = configuration.GridSize; float lX = gridSize.x; float lY = gridSize.y; var waterMap = new SimpleHeightArray(heightMap.Width, heightMap.Height); var waterMap_1 = new SimpleHeightArray(heightMap.Width, heightMap.Height); var waterMap_2 = new SimpleHeightArray(heightMap.Width, heightMap.Height); var fluxMap = new Vector4ArrayTODO(heightMap.Width, heightMap.Height); var velocityMap = new VectorArrayTODO(heightMap.Width, heightMap.Height); var sedimentMap = new SimpleHeightArray(heightMap.Width, heightMap.Height); var sedimentMap_1 = new SimpleHeightArray(heightMap.Width, heightMap.Height); for (int i = 0; i < stepCount; i++) { if (i >= 0) { // water increment for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var aPoint = new IntVector2(x, y); var oldValue = waterMap.GetValue(aPoint); var newAmount = oldValue + deltaT * constantWaterAdding; waterMap_1.SetValue(aPoint, newAmount); if (x == 10 && y == 10) { var settings = DebugGetMapValues(x, y, waterMap, waterMap_1, waterMap_2, fluxMap, velocityMap, sedimentMap, sedimentMap_1); int yyy = 2; } } } } else { MyArrayUtils.Copy(waterMap.Array, waterMap_1.Array); } debugOutput.AddArray("waterMap", SimpleHeightArray.ToHeightmap(waterMap_1), i); debugOutput.AddArray("heightMap", SimpleHeightArray.ToHeightmap(heightMap), i); debugOutput.AddArray("sedimentMap", SimpleHeightArray.ToHeightmap(sedimentMap), i); var speedMap = new float[sedimentMap.Width, sedimentMap.Height]; for (int x = 0; x < sedimentMap.Width; x++) { for (int y = 0; y < sedimentMap.Height; y++) { speedMap[x, y] = velocityMap.GetValue(x, y).magnitude; } } debugOutput.AddArray("speedMap", new HeightmapArray(speedMap), i); // flow simulation int onesCount = 0; for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var aPoint = new IntVector2(x, y); if (x == 10 && y == 10) { var settings = DebugGetMapValues(x, y, waterMap, waterMap_1, waterMap_2, fluxMap, velocityMap, sedimentMap, sedimentMap_1); Debug.Log("T44. W0: " + waterMap.GetValue(aPoint) + " w1: " + waterMap_1.GetValue(aPoint)); int yyy = 2; } Vector4 newFlux = Vector4.zero; var neighbours = GetNeighbours(heightMap, aPoint); foreach (var neighbour in neighbours) { var d_difference = (heightMap.GetValue(aPoint) + waterMap_1.GetValue(aPoint)) - (heightMap.GetValue(neighbour.Position) + waterMap_1.GetValue(neighbour.Position)); var d_flux = fluxMap.GetValue(aPoint)[(int)neighbour.DirectionIndex]; var d_new_flux = Mathf.Max(0, d_flux + deltaT * A_pipeCrossSection * (g_GravityAcceleration * d_difference) / l_pipeLength); Preconditions.Assert(!float.IsNaN(d_new_flux), ""); newFlux[(int)neighbour.DirectionIndex] = d_new_flux; } var aD1 = waterMap_1.GetValue(aPoint); var fluxSum = VectorUtils.SumMembers(newFlux); float K_factor = 0; if (fluxSum != 0) { K_factor = Mathf.Min(1, aD1 * (lX * lY) / (fluxSum * deltaT)); } if (K_factor > 0.99999) { onesCount++; } fluxMap.SetValue(aPoint, K_factor * newFlux); } } Debug.Log("t66: onesCount is " + onesCount); // velocity calculation for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var aPoint = new IntVector2(x, y); if (x == 10 && y == 10) { var settings = DebugGetMapValues(x, y, waterMap, waterMap_1, waterMap_2, fluxMap, velocityMap, sedimentMap, sedimentMap_1); int yyy = 2; } var neighbours = GetNeighbours(heightMap, aPoint); var outFlow = VectorUtils.SumMembers(fluxMap.GetValue(aPoint)); var inFlow = neighbours.Select(c => fluxMap.GetValue(c.Position)[(int)c.DirectionIndex.GetOpposite()]) .Sum(); var aChangeOfWater = deltaT * (inFlow - outFlow); waterMap_2.SetValue(aPoint, Mathf.Max(0, waterMap_1.GetValue(aPoint) + aChangeOfWater / (lX * lY))); var horizontalFlux = GetFlux(fluxMap, neighbours, MaiNeighbourDirection.LEFT, MaiNeighbourDirection.RIGHT) - fluxMap.GetValue(aPoint)[(int)MaiNeighbourDirection.LEFT] + fluxMap.GetValue(aPoint)[(int)MaiNeighbourDirection.RIGHT] - GetFlux(fluxMap, neighbours, MaiNeighbourDirection.RIGHT, MaiNeighbourDirection.LEFT); var deltaWx = horizontalFlux / 2; var verticalFlux = GetFlux(fluxMap, neighbours, MaiNeighbourDirection.DOWN, MaiNeighbourDirection.UP) - fluxMap.GetValue(aPoint)[(int)MaiNeighbourDirection.DOWN] + fluxMap.GetValue(aPoint)[(int)MaiNeighbourDirection.UP] - GetFlux(fluxMap, neighbours, MaiNeighbourDirection.UP, MaiNeighbourDirection.DOWN); var deltaWy = verticalFlux / 2; var avgHeight = (waterMap_1.GetValue(aPoint) + waterMap_2.GetValue(aPoint)) / 2; var newVelocity = new Vector2( (deltaWx / (lX * avgHeight)), (deltaWy / (lY * avgHeight)) ); if (float.IsNaN(newVelocity.magnitude)) { newVelocity = Vector2.zero; } velocityMap.SetValue(aPoint, newVelocity); //todo ograniczenie CFL } } var erodedAmountMap = new float[sedimentMap.Width, sedimentMap.Height]; var depositionAmountMap = new float[sedimentMap.Width, sedimentMap.Height]; // sediment calc for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var aPoint = new IntVector2(x, y); if (x == 10 && y == 10) { var settings = DebugGetMapValues(x, y, waterMap, waterMap_1, waterMap_2, fluxMap, velocityMap, sedimentMap, sedimentMap_1); int yyy = 2; } var aHeight = heightMap.GetValue(aPoint); Vector3 upDir = new Vector3(0, 0, lY); if (y != heightMap.Height - 1) { var upHeight = heightMap.GetValue(aPoint + new IntVector2(0, 1)); upDir[1] = upHeight - aHeight; } Vector3 rightDir = new Vector3(lX, 0, 0); if (x != heightMap.Width - 1) { var rightHeight = heightMap.GetValue(aPoint + new IntVector2(1, 0)); rightDir[1] = rightHeight - aHeight; } var aNormal = Vector3.Cross(upDir, rightDir); var baseNormal = new Vector3(0, 1, 0); var cosAlpha = Vector3.Dot(baseNormal, aNormal) / (baseNormal.magnitude * aNormal.magnitude); var sinAlpha = Math.Sqrt(1 - cosAlpha * cosAlpha); //////// OTHER CALCULATING var _o_notmal = new Vector3( heightMap.GetValueWithZeroInMissing(y, x + 1) - heightMap.GetValueWithZeroInMissing(y, x - 1), heightMap.GetValueWithZeroInMissing(y + 1, x) - heightMap.GetValueWithZeroInMissing(y - 1, x), 2); var o_normal2 = _o_notmal.normalized; Vector3 up = new Vector3(0, 1, 0); float cosa = Vector3.Dot(o_normal2, up); float o_sinAlpha = Mathf.Sin(Mathf.Acos(cosa)); // todo kontrolne zwiększenie wody var capacity = kc_SedimentCapacityConstant * Mathf.Max(o_sinAlpha, 0.1f) * velocityMap.GetValue(aPoint).magnitude; //todo set minimum alpha var suspendedSediment = sedimentMap.GetValue(aPoint); erodedAmountMap[x, y] = 0; depositionAmountMap[x, y] = 0; if (capacity > suspendedSediment) { var sedimentChangeAmount = (float)(ks_DissolvingConstant * (capacity - suspendedSediment)); erodedAmountMap[x, y] = sedimentChangeAmount; heightMap.AddValue(aPoint, -sedimentChangeAmount); sedimentMap_1.AddValue(aPoint, sedimentChangeAmount); } else { var sedimentChangeAmount = (float)(kd_DepositionConstant * (suspendedSediment - capacity)); depositionAmountMap[x, y] = sedimentChangeAmount; heightMap.AddValue(aPoint, sedimentChangeAmount); // sedimentChangeAmount jest ujemna, więc dodajemy teren sedimentMap_1.AddValue(aPoint, -sedimentChangeAmount); } } } debugOutput.AddArray("erodedAmount", new HeightmapArray(erodedAmountMap), i); debugOutput.AddArray("depositedAmount", new HeightmapArray(depositionAmountMap), i); // sediment transportation for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var aPoint = new IntVector2(x, y); if (x == 10 && y == 10) { var settings = DebugGetMapValues(x, y, waterMap, waterMap_1, waterMap_2, fluxMap, velocityMap, sedimentMap, sedimentMap_1); int yyy = 2; } var velocity = velocityMap.GetValue(aPoint); sedimentMap.SetValue(aPoint, sedimentMap_1.GetValueWithIndexClamped(aPoint.ToFloatVec() - velocity * deltaT)); } } //evaporation for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var aPoint = new IntVector2(x, y); if (x == 10 && y == 10) { var settings = DebugGetMapValues(x, y, waterMap, waterMap_1, waterMap_2, fluxMap, velocityMap, sedimentMap, sedimentMap_1); int yyy = 2; } waterMap.SetValue(aPoint, waterMap_2.GetValue(aPoint) * (1 - ke_EvaporationConstant * deltaT)); } } } //for (int y = 0; y < heightMap.Height; y++) //{ // for (int x = 0; x < heightMap.Width; x++) // { // var aPoint = new IntVector2(x,y); // heightMap.AddValue(aPoint, sedimentMap.GetValue(aPoint)); // } //} //Debug.Log("t23: " + sb.ToString()); return(debugOutput); }
public void Erode(SimpleHeightArray heightMap, MeiHydraulicEroderConfiguration configuration) { //int stepCount = configuration.StepCount; //float deltaT = configuration.DeltaT; //float constantWaterAdding = configuration.ConstantWaterAdding; //float A_pipeCrossSection = configuration.A_PipeCrossSection; //float l_pipeLength = configuration.L_PipeLength; //float g_GravityAcceleration = configuration.GravityAcceleration; //float ks_DissolvingConstant = configuration.DissolvingConstant; //float kd_DepositionConstant = configuration.DepositionConstant; //float ke_EvaporationConstant = configuration.EvaporationConstant; //float kc_SedimentCapacityConstant = configuration.SedimentCapacityConstant; //Vector2 gridSize = configuration.GridSize; int stepCount = configuration.StepCount; float deltaT = configuration.DeltaT; float constantWaterAdding = configuration.ConstantWaterAdding; float A_pipeCrossSection = configuration.A_PipeCrossSection; float l_pipeLength = configuration.L_PipeLength; float g_GravityAcceleration = configuration.GravityAcceleration; float ks_DissolvingConstant = configuration.DissolvingConstant; float kd_DepositionConstant = configuration.DepositionConstant; float ke_EvaporationConstant = configuration.EvaporationConstant; float kc_SedimentCapacityConstant = configuration.SedimentCapacityConstant; Vector2 gridSize = configuration.GridSize; float lX = gridSize.x; float lY = gridSize.y; var waterMap = new SimpleHeightArray(heightMap.Width, heightMap.Height); var waterMap_1 = new SimpleHeightArray(heightMap.Width, heightMap.Height); var waterMap_2 = new SimpleHeightArray(heightMap.Width, heightMap.Height); var fluxMap = new MySimpleArray <Vector4>(heightMap.Width, heightMap.Height); var velocityMap = new MySimpleArray <Vector2>(heightMap.Width, heightMap.Height); var sedimentMap = new SimpleHeightArray(heightMap.Width, heightMap.Height); var sedimentMap_1 = new SimpleHeightArray(heightMap.Width, heightMap.Height); for (int i = 0; i < stepCount; i++) { // water increment for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var aPoint = new IntVector2(x, y); waterMap_1.SetValue(aPoint, waterMap.GetValue(aPoint) + deltaT * constantWaterAdding); } } // flow simulation for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var aPoint = new IntVector2(x, y); if (x == 10 && y == 10) { int yyy = 2; } Vector4 newFlux = Vector4.zero; var neighbours = GetNeighbours(heightMap, aPoint); foreach (var neighbour in neighbours) { var d_difference = (heightMap.GetValue(aPoint) + waterMap_1.GetValue(aPoint)) - (heightMap.GetValue(neighbour.Position) + waterMap_1.GetValue(neighbour.Position)); var d_flux = fluxMap.GetValue(aPoint)[(int)neighbour.DirectionIndex]; var d_new_flux = Mathf.Max(0, d_flux + deltaT * A_pipeCrossSection * (g_GravityAcceleration * d_difference) / l_pipeLength); newFlux[(int)neighbour.DirectionIndex] = d_new_flux; } var aD1 = waterMap_1.GetValue(aPoint); var K_factor = Mathf.Min(1, aD1 * (lX * lY) / ((newFlux[0] + newFlux[1] + newFlux[2] + newFlux[3]) * deltaT)); fluxMap.SetValue(aPoint, K_factor * newFlux); } } // velocity calculation for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var aPoint = new IntVector2(x, y); if (x == 10 && y == 10) { int yyy = 2; } var neighbours = GetNeighbours(heightMap, aPoint); var outFlow = VectorUtils.SumMembers(fluxMap.GetValue(aPoint)); var inFlow = neighbours.Select(c => fluxMap.GetValue(c.Position)[(int)c.DirectionIndex.GetOpposite()]) .Sum(); var aChangeOfWater = deltaT * (inFlow - outFlow); waterMap_2.SetValue(aPoint, Mathf.Max(0, waterMap_1.GetValue(aPoint) + aChangeOfWater / (lX * lY))); var horizontalFlux = GetFlux(fluxMap, neighbours, MaiNeighbourDirection.LEFT, MaiNeighbourDirection.RIGHT) - fluxMap.GetValue(aPoint)[(int)MaiNeighbourDirection.LEFT] + fluxMap.GetValue(aPoint)[(int)MaiNeighbourDirection.RIGHT] - GetFlux(fluxMap, neighbours, MaiNeighbourDirection.RIGHT, MaiNeighbourDirection.LEFT); var deltaWx = horizontalFlux / 2; var verticalFlux = GetFlux(fluxMap, neighbours, MaiNeighbourDirection.DOWN, MaiNeighbourDirection.UP) - fluxMap.GetValue(aPoint)[(int)MaiNeighbourDirection.DOWN] + fluxMap.GetValue(aPoint)[(int)MaiNeighbourDirection.UP] - GetFlux(fluxMap, neighbours, MaiNeighbourDirection.UP, MaiNeighbourDirection.DOWN); var deltaWy = verticalFlux / 2; var avgHeight = (waterMap_1.GetValue(aPoint) + waterMap_2.GetValue(aPoint)) / 2; var newVelocity = new Vector2( (deltaWx / (lX * avgHeight)), (deltaWy / (lY * avgHeight)) ); velocityMap.SetValue(aPoint, newVelocity); //todo ograniczenie CFL } } // sediment calc for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var aPoint = new IntVector2(x, y); if (x == 10 && y == 10) { int yyy = 2; } var aHeight = heightMap.GetValue(aPoint); Vector3 upDir = new Vector3(0, 0, lY); if (y != heightMap.Height - 1) { var upHeight = heightMap.GetValue(aPoint + new IntVector2(0, 1)); upDir[1] = upHeight - aHeight; } Vector3 rightDir = new Vector3(lX, 0, 0); if (x != heightMap.Width - 1) { var rightHeight = heightMap.GetValue(aPoint + new IntVector2(1, 0)); rightDir[1] = rightHeight - aHeight; } var aNormal = Vector3.Cross(upDir, rightDir); var baseNormal = new Vector3(0, 1, 0); var cosAlpha = Vector3.Dot(baseNormal, aNormal) / (baseNormal.magnitude * aNormal.magnitude); var sinAlpha = Math.Sqrt(1 - cosAlpha * cosAlpha); //////// OTHER CALCULATING var _o_notmal = new Vector3( heightMap.GetValueWithZeroInMissing(y, x + 1) - heightMap.GetValueWithZeroInMissing(y, x - 1), heightMap.GetValueWithZeroInMissing(y + 1, x) - heightMap.GetValueWithZeroInMissing(y - 1, x), 2); var o_normal2 = _o_notmal.normalized; Vector3 up = new Vector3(0, 1, 0); float cosa = Vector3.Dot(o_normal2, up); float o_sinAlpha = Mathf.Sin(Mathf.Acos(cosa)); // todo kontrolne zwiększenie wody var capacity = kc_SedimentCapacityConstant * sinAlpha * velocityMap.GetValue(aPoint).magnitude; //todo set minimum alpha var suspendedSediment = sedimentMap.GetValue(aPoint); if (capacity > suspendedSediment) { var sedimentChangeAmount = (float)(ks_DissolvingConstant * (capacity - suspendedSediment)); heightMap.AddValue(aPoint, -sedimentChangeAmount); sedimentMap_1.AddValue(aPoint, sedimentChangeAmount); } else { var sedimentChangeAmount = (float)(kd_DepositionConstant * (suspendedSediment - capacity)); heightMap.AddValue(aPoint, sedimentChangeAmount); // sedimentChangeAmount jest ujemna, więc dodajemy teren sedimentMap_1.AddValue(aPoint, -sedimentChangeAmount); } } } // sediment transportation for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var aPoint = new IntVector2(x, y); if (x == 10 && y == 10) { int yyy = 2; } var velocity = velocityMap.GetValue(aPoint); sedimentMap.SetValue(aPoint, sedimentMap_1.GetValueWithIndexClamped(aPoint.ToFloatVec() - velocity * deltaT)); } } //evaporation for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var aPoint = new IntVector2(x, y); if (x == 10 && y == 10) { int yyy = 2; } waterMap.SetValue(aPoint, waterMap_2.GetValue(aPoint) * (1 - ke_EvaporationConstant * deltaT)); } } } //for (int y = 0; y < heightMap.Height; y++) //{ // for (int x = 0; x < heightMap.Width; x++) // { // var aPoint = new IntVector2(x,y); // heightMap.AddValue(aPoint, sedimentMap.GetValue(aPoint)); // } //} }
public DebuggableHydraulicEroderOutput Erode(SimpleHeightArray heightMap, HydraulicEroderConfiguration configuration, int snapshotFrequencies) { List <SimpleHeightArray> WaterSnapshots = new List <SimpleHeightArray>(); List <SimpleHeightArray> SedimentSnapshots = new List <SimpleHeightArray>(); float krParam = configuration.kr_ConstantWaterAddition; float ksParam = configuration.ks_GroundToSedimentFactor; float keParam = configuration.ke_WaterEvaporationFactor; float kcParam = configuration.kc_MaxSedimentationFactor; bool finalSedimentationToGround = configuration.FinalSedimentationToGround; int stepCount = configuration.StepCount; ErodedNeighbourFinder neighbourFinder = configuration.NeighbourFinder; var waterMap = new SimpleHeightArray(heightMap.Width, heightMap.Height); var sedimentMap = new SimpleHeightArray(heightMap.Width, heightMap.Height); var sedimentAdditionMap = new SimpleHeightArray(heightMap.Width, heightMap.Height); var waterAdditionMap = new SimpleHeightArray(heightMap.Width, heightMap.Height); for (int i = 0; i < stepCount; i++) { for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var point = new IntVector2(x, y); if (configuration.WaterGenerator == HydraulicEroderWaterGenerator.FirstFrame) { if (i == 0) { waterMap.AddValue(point, krParam); } } else { waterMap.AddValue(point, krParam); } float amountChangedToSediment = ksParam * waterMap.GetValue(point); heightMap.AddValue(point, -amountChangedToSediment); sedimentMap.AddValue(point, amountChangedToSediment); } } if (i == 0) { WaterSnapshots.Add(new SimpleHeightArray(MyArrayUtils.DeepClone <float>(waterMap.Array))); SedimentSnapshots.Add(new SimpleHeightArray(MyArrayUtils.DeepClone <float>(sedimentMap.Array))); } for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var point = new IntVector2(x, y); var neighbourPoints = neighbourFinder.Find(heightMap, point); var pTotalHeight = heightMap.GetValue(point) + waterMap.GetValue(point); var neighbours = neighbourPoints.Select(nPoint => new { Point = nPoint, TotalHeight = heightMap.GetValue(nPoint) + waterMap.GetValue(nPoint), TotalHeightDifference = pTotalHeight - (heightMap.GetValue(nPoint) + waterMap.GetValue(nPoint)), }).Where(c => c.TotalHeightDifference > 0).ToList(); if (!neighbours.Any()) { continue; } var dTotalHeightDiffSum = neighbours.Sum(c => c.TotalHeightDifference); var avgTotalHeight = (neighbours.Sum(c => c.TotalHeight) + pTotalHeight) / (neighbours.Count + 1); var pSediment = sedimentMap.GetValue(point); var pWater = waterMap.GetValue(point); if (pWater < 0.00000001f) { continue; } var pDeltaA = pTotalHeight - avgTotalHeight; var pMin = Mathf.Min(pDeltaA, pWater); if (configuration.DestinationFinder == HydraulicEroderWaterDestinationFinder.OnlyBest) { var bestNeighbour = neighbours.OrderByDescending(c => c.TotalHeightDifference).First(); var nMovedWater = pMin; var addedSediment = pSediment * (nMovedWater / pWater); waterAdditionMap.AddValue(bestNeighbour.Point, nMovedWater); sedimentAdditionMap.AddValue(bestNeighbour.Point, addedSediment); sedimentAdditionMap.AddValue(point, -addedSediment); waterAdditionMap.AddValue(point, -pMin); } else { var movedSedimentSum = 0f; foreach (var aNeighbour in neighbours) { var nMovedWater = pMin * (aNeighbour.TotalHeightDifference / dTotalHeightDiffSum); var addedSediment = pSediment * (nMovedWater / pWater); movedSedimentSum += addedSediment; waterAdditionMap.AddValue(aNeighbour.Point, nMovedWater); sedimentAdditionMap.AddValue(aNeighbour.Point, addedSediment); } sedimentAdditionMap.AddValue(point, -movedSedimentSum); waterAdditionMap.AddValue(point, -pMin); } } } for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var point = new IntVector2(x, y); waterMap.AddValue(point, waterAdditionMap.GetValue(point)); waterAdditionMap.SetValue(point, 0); sedimentMap.AddValue(point, sedimentAdditionMap.GetValue(point)); sedimentAdditionMap.SetValue(point, 0); } } for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var point = new IntVector2(x, y); var pWater = waterMap.GetValue(point); var waterAfterEvaporation = pWater * (1 - keParam); waterMap.SetValue(point, waterAfterEvaporation); var pSedimentMax = kcParam * waterAfterEvaporation; var pSediment = sedimentMap.GetValue(point); var deltaSediment = Mathf.Max(0, pSediment - pSedimentMax); sedimentMap.AddValue(point, -deltaSediment); heightMap.AddValue(point, deltaSediment); } } if (i % snapshotFrequencies == 0) { WaterSnapshots.Add(new SimpleHeightArray(MyArrayUtils.DeepClone <float>(waterMap.Array))); SedimentSnapshots.Add(new SimpleHeightArray(MyArrayUtils.DeepClone <float>(sedimentMap.Array))); } } if (finalSedimentationToGround) { for (int y = 0; y < heightMap.Height; y++) { for (int x = 0; x < heightMap.Width; x++) { var point = new IntVector2(x, y); heightMap.AddValue(point, sedimentMap.GetValue(point)); } } } return(new DebuggableHydraulicEroderOutput() { SedimentSnapshots = SedimentSnapshots, WaterSnapshots = WaterSnapshots }); }
public void Erode(SimpleHeightArray inHeightArray, ThermalErosionConfiguration configuration) { var tParam = configuration.TParam; var cParam = configuration.CParam; var stepCount = configuration.StepCount; var finalDifferenceArray = new SimpleHeightArray(inHeightArray.Width, inHeightArray.Height); for (int stepIndex = 0; stepIndex < stepCount; stepIndex++) { Parallel.For <SimpleHeightArray>(0, inHeightArray.Height, new ParallelOptions { MaxDegreeOfParallelism = 4 }, () => new SimpleHeightArray(inHeightArray.Width, inHeightArray.Height), (y, loop, localDifferenceArray) => { // Parallel.For(0, inHeightArray.Height,new ParallelOptions { MaxDegreeOfParallelism = 3 }, y => // for (int y = 0; y < inHeightArray.Height; y++) // { for (int x = 0; x < inHeightArray.Width; x++) { var point = new IntVector2(x, y); var thisValue = inHeightArray.GetValue(point); var neighbours = configuration.NeighbourFinder.Find(inHeightArray, point).Select(n => new { point = n, difference = thisValue - inHeightArray.GetValue(n) }).ToList(); neighbours = neighbours.Where(c => c.difference < tParam && c.difference > 0).ToList(); if (!neighbours.Any()) { continue; } var dTotal = neighbours.Sum(c => c.difference); var dMax = neighbours.Max(c => c.difference); foreach (var aNeighbour in neighbours) { var movedGround = cParam * (dMax) * (aNeighbour.difference / dTotal); //finalDifferenceArray.AddValue(aNeighbour.point, movedGround); //finalDifferenceArray.AddValue(point, -movedGround); localDifferenceArray.AddValue(aNeighbour.point, movedGround); localDifferenceArray.AddValue(point, -movedGround); } } return(localDifferenceArray); }, (localDifferenceArray) => { finalDifferenceArray.SumValue(localDifferenceArray); }); for (int y = 0; y < inHeightArray.Height; y++) { for (int x = 0; x < inHeightArray.Width; x++) { var point = new IntVector2(x, y); inHeightArray.AddValue(point, finalDifferenceArray.GetValue(point)); finalDifferenceArray.SetValue(point, 0); } } } }