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 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 Start_Mai_Debug() { var heightTexture1 = SavingFileManager.LoadPngTextureFromFile(@"C:\inz\cont\smallCut.png", 240, 240, TextureFormat.RGBA32, true, true); var heightmap1 = HeightmapUtils.CreateHeightmapArrayFromTexture(heightTexture1); //DiamondSquareCreator creator = new DiamondSquareCreator(new RandomProvider(22)); //var heightmap2 = creator.CreateDiamondSquareNoiseArray(new IntVector2(240, 240), 64); //MyArrayUtils.Multiply(heightmap2.HeightmapAsArray, 0.1f); //HeightmapArray workingHeightmap = LoadHeightmapFromTextureFile(@"C:\inz\cont\temp3.png"); HeightmapArray workingHeightmap = heightmap1; MyArrayUtils.Normalize(workingHeightmap.HeightmapAsArray); MyArrayUtils.InvertNormalized(workingHeightmap.HeightmapAsArray); HeightmapArray originalMap = new HeightmapArray(MyArrayUtils.DeepClone(workingHeightmap.HeightmapAsArray)); MyArrayUtils.Multiply(workingHeightmap.HeightmapAsArray, 400); var configuration = new MeiHydraulicEroderConfiguration() { StepCount = 50, A_PipeCrossSection = 0.05f, ConstantWaterAdding = 1 / 64f, GravityAcceleration = 9.81f, DeltaT = 1f, DepositionConstant = 0.0001f * 12 * 2f, DissolvingConstant = 0.0001f * 12 * 2f, EvaporationConstant = 0.05f * 10, GridSize = new Vector2(1, 1), L_PipeLength = 1, SedimentCapacityConstant = 250 }; var eroder = new MeiHydraulicEroder(); var debOutput = eroder.ErodeWithDebug(SimpleHeightArray.FromHeightmap(workingHeightmap), configuration); MyArrayUtils.Multiply(workingHeightmap.HeightmapAsArray, 1f / 400); debOutput.NormalizeInGroups(); _go = GameObject.CreatePrimitive(PrimitiveType.Quad); var material = new Material(Shader.Find("Custom/Terrain/Terrain_Mei_Debug_Comparision_StepByStep")); _go.GetComponent <MeshRenderer>().material = material; _go.name = "Terrain"; _go.transform.localRotation = Quaternion.Euler(0, 0, 0); _go.transform.localScale = new Vector3(10, 1, 10); _go.transform.localPosition = new Vector3(0, 0, 0); _go.GetComponent <MeshFilter>().mesh = PlaneGenerator.CreateFlatPlaneMesh(240, 240); MyHeightTextureArray heightTextureArray = new MyHeightTextureArray(240, 240, 2, TextureFormat.ARGB32, false, true); heightTextureArray.AddElementArray(originalMap, 0); heightTextureArray.AddElementArray(workingHeightmap, 1); _go.GetComponent <MeshRenderer>().material .SetTexture("_HeightmapTexArray", heightTextureArray.ApplyAndRetrive()); var arrayListsCount = debOutput.OneArrayListCount; var arrayListsLength = debOutput.OneArrayListLength; MyHeightTextureArray detailHeightTexturesArray = new MyHeightTextureArray(240, 240, arrayListsCount * arrayListsLength, TextureFormat.ARGB32, false, true); foreach (var snapshot in debOutput.ArraysDict.Values.SelectMany(c => c)) { detailHeightTexturesArray.AddElementArray(snapshot); } _go.GetComponent <MeshRenderer>().material .SetTexture("_DetailTexArray", detailHeightTexturesArray.ApplyAndRetrive()); _go.GetComponent <MeshRenderer>().material.SetFloat("_DetailTexLength", arrayListsLength); }