private void GroundToSediment() { if (Configs.TerrainSolubility == 0) { return; } for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { float waterVolume = WaterMap[x, y] - SoilMap[x, y]; if (waterVolume <= 0) { continue; } float amountToRemove = Configs.TerrainSolubility * waterVolume; amountToRemove = Math.Min(amountToRemove, SoilMap[x, y] - RockMap[x, y]); SoilMap[x, y] -= amountToRemove; // TODO: Talvez a água pudesse converter a camada de rocha para sedimento caso não haja solo suficiente para atingir a saturação geral } } UpdateMeshes = true; UpdateShades = true; }
public void CalculateHeat() { if (HeatMap == null || HeatMap.GetLength(0) != SoilMap.GetLength(0) || HeatMap.GetLength(1) != SoilMap.GetLength(1)) { HeatMap = new float[SoilMap.GetLength(0), SoilMap.GetLength(1)]; } switch (Type) { case HeatTypes.SoilDepth: CalculateSoilDepth(); break; case HeatTypes.WaterDepth: CalculateWaterDepth(); break; case HeatTypes.SoilHumidity: CalculateSoilHumidity(); break; case HeatTypes.Inclination: CalculateInclination(); break; } }
private void DrainWater() { if (Configs.EvaporationFactor == 0) { return; } float evaporationPercent = (1 - Configs.EvaporationFactor); for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { float waterVolume = WaterMap[x, y] - SoilMap[x, y]; if (waterVolume <= 0) { continue; } float diff = waterVolume - (waterVolume * evaporationPercent); diff *= SurfaceDrainModifiers[SurfaceMap[x, y]]; WaterMap[x, y] -= diff; SoilMap[x, y] += Configs.TerrainSolubility * diff; HumidityMap[x, y] += diff; if (HumidityMap[x, y] > 1.0f) { HumidityMap[x, y] = 1.0f; } } } UpdateMeshes = true; UpdateShades = true; }
// As inclinações são avaliadas de cima para baixo private List<float> GetLocalInclines(int x, int y) { List<float> inclines = new List<float>(); if (x != 0) { float incline = SoilMap[x, y] - SoilMap[x - 1, y]; if (incline > 0) inclines.Add(incline); } if (y != 0) { float incline = SoilMap[x, y] - SoilMap[x, y - 1]; if (incline > 0) inclines.Add(incline); } if (x != SoilMap.GetLength(0) - 1) { float incline = SoilMap[x, y] - SoilMap[x + 1, y]; if (incline > 0) inclines.Add(incline); } if (y != SoilMap.GetLength(1) - 1) { float incline = SoilMap[x, y] - SoilMap[x, y + 1]; if (incline > 0) inclines.Add(incline); } return inclines; }
private void SetSoilType(Vector3 point) { int maxX = SoilMap.GetLength(0); int maxZ = SoilMap.GetLength(1); int begX = (int)((point.x / soilLayer.terrainData.size.x) * maxX); int begZ = (int)((point.z / soilLayer.terrainData.size.z) * maxZ); begX -= EditConfigs.BrushSize; begZ -= EditConfigs.BrushSize; int end = EditConfigs.BrushSize * 2; int type = (int)EditConfigs.SurfacePaintMode; for (int x = 0; x < end; ++x) { int curX = begX + x; if (curX < 0 || curX >= maxX) { continue; } for (int z = 0; z < end; ++z) { int curZ = begZ + z; if (curZ < 0 || curZ >= maxZ) { continue; } SurfaceMap[curZ, curX] = type; } } }
private void TransformVonNeumann() { // Transformação usando vizinhança Von Neumann int topX = SoilMap.GetLength(0); int topY = SoilMap.GetLength(1); float[,] baseHeights = SoilMap.Clone() as float[, ]; // Loop geral do mapa for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { // Fazer a média da altura com base nos vizinhos float sumHeights = 0.0f; int countHeights = 0; // Primeiro somar os vizinhos na horizontal for (int relX = -Configs.Range; relX <= Configs.Range; relX++) { int absX = x + relX; if (absX < 0 || absX >= topX) { continue; } sumHeights += baseHeights[absX, y]; countHeights++; } // Depois na vertical for (int relY = -Configs.Range; relY <= Configs.Range; relY++) { int absY = y + relY; if (absY < 0 || absY >= topY) { continue; } sumHeights += baseHeights[x, absY]; countHeights++; } // Subtrair o valor da célula central que foi somado duas vezes sumHeights -= baseHeights[x, y]; countHeights--; // Aplicar a média dos valores if (countHeights > 0) { float diff = (sumHeights / countHeights) - SoilMap[x, y]; SoilMap[x, y] += diff * Configs.Factor; } } } }
private void LowerTerrain(Vector3 point) { // Método para referência https://forum.unity3d.com/threads/edit-terrain-in-real-time.98410/ int terX = (int)((point.x / soilLayer.terrainData.size.x) * SoilMap.GetLength(0)); int terZ = (int)((point.z / soilLayer.terrainData.size.z) * SoilMap.GetLength(1)); float y = SoilMap[terX, terZ]; y -= 0.001f; float[,] height = new float[1, 1]; height[0, 0] = y; SoilMap[terX, terZ] = y; soilLayer.terrainData.SetHeights(terX, terZ, height); }
private void TransformMoore() { // Transformação usando vizinhança Moore int topX = SoilMap.GetLength(0); int topY = SoilMap.GetLength(1); float[,] baseHeights = SoilMap.Clone() as float[, ]; // Loop geral do mapa for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { // Fazer a média da altura com base nos vizinhos float sumHeights = 0.0f; int countHeights = 0; // Loop interno dos vizinhos for (int relX = -Configs.Range; relX <= Configs.Range; relX++) { int absX = x + relX; if (absX < 0 || absX >= topX) { continue; } for (int relY = -Configs.Range; relY <= Configs.Range; relY++) { int absY = y + relY; if (absY < 0 || absY >= topY) { continue; } sumHeights += baseHeights[absX, absY]; countHeights++; } } // Aplicar a média dos valores if (countHeights > 0) { float diff = (sumHeights / countHeights) - SoilMap[x, y]; SoilMap[x, y] += diff * Configs.Factor; } } } }
private void CalculateWaterDepth() { for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { float value = (WaterMap[x, y] - SoilMap[x, y]) * 10; if (value < 0) { value = 0; } else if (value > 1.0f) { value = 1.0f; } HeatMap[x, y] = value; } } }
private void CalculateInclination() { for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { float value = GetHighestIncline(x, y) * 50; if (value < 0) { value = 0; } else if (value > 1.0f) { value = 1.0f; } HeatMap[x, y] = value; } } }
private void CalculateSoilHumidity() { for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { float value = HumidityMap[x, y] * 10; if (value < 0) { value = 0; } else if (value > 1.0f) { value = 1.0f; } HeatMap[x, y] = value; } } }
public void LoadMaps() { rockLayer.terrainData.heightmapResolution = RockMap.GetLength(0); rockLayer.terrainData.SetHeights(0, 0, RockMap); soilLayer.terrainData.heightmapResolution = SoilMap.GetLength(0); soilLayer.terrainData.alphamapResolution = SoilMap.GetLength(0); soilLayer.terrainData.SetHeights(0, 0, SoilMap); waterLayer.terrainData.heightmapResolution = SoilMap.GetLength(0); waterLayer.terrainData.alphamapResolution = SoilMap.GetLength(0); waterLayer.terrainData.SetHeights(0, 0, WaterMap); heatLayer.terrainData.heightmapResolution = SoilMap.GetLength(0); heatLayer.terrainData.alphamapResolution = SoilMap.GetLength(0); heatLayer.terrainData.SetHeights(0, 0, SoilMap); ResetAllTransforms(); UpdateView(true); }
private float GetHighestIncline(int x, int y) { float highest = 0; if (x != 0) { float incline = SoilMap[x, y] - SoilMap[x - 1, y]; if (incline > highest) { highest = incline; } } if (y != 0) { float incline = SoilMap[x, y] - SoilMap[x, y - 1]; if (incline > highest) { highest = incline; } } if (x != SoilMap.GetLength(0) - 1) { float incline = SoilMap[x, y] - SoilMap[x + 1, y]; if (incline > highest) { highest = incline; } } if (y != SoilMap.GetLength(1) - 1) { float incline = SoilMap[x, y] - SoilMap[x, y + 1]; if (incline > highest) { highest = incline; } } return(highest); }
public void ApplyMoore() { int topX = SoilMap.GetLength(0); int topY = SoilMap.GetLength(1); float[,] baseHeights = SoilMap.Clone() as float[, ]; int startX = GetStartingX(); int startY = GetStartingY(); int endX = GetEndingX(); int endY = GetEndingY(); int incStartY = GetStartingYIncrement(); int incEndY = GetEndingYIncrement(); for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { // Fazer a média da altura com base nas alturas vizinhas no hemisfério Sul // Apenas considerar alterações para baixo float sumHeights = 0.0f; int countHeights = 0; int localStartY = startY; int localEndY = endY; for (int relX = startX; relX <= endX; relX++) { int absX = x + relX; if (absX < 0 || absX >= topX) { continue; } for (int relY = localStartY; relY <= localEndY; relY++) { int absY = y + relY; if (absY < 0 || absY >= topY) { continue; } sumHeights += baseHeights[absX, absY]; countHeights++; } localStartY += incStartY; localEndY += incEndY; } if (countHeights > 0) { float avg = sumHeights / countHeights; if (avg < SoilMap[x, y]) { float diff = avg - SoilMap[x, y]; SoilMap[x, y] += diff * Configs.Factor; } } } } }
public void CollectStats() { float soilMass = 0; float waterMass = 0; float humidityMass = 0; float highSoil = 0; float highWater = 0; float highHumidity = 0; float highHeight = 0; float highIncline = 0; float lowSoil = 0; float lowWater = 0; float lowHumidity = 0; float lowHeight = 0; float lowIncline = 1; float avgSoil = 0; float avgWater = 0; float avgHumidity = 0; float avgHeight = 0; float avgIncline = 0; float totalHeight = 0; float totalIncline = 0; int inclineCount = 0; int[] surfaceCounter = new int[(int)SurfaceType.Count]; int topX = SoilMap.GetLength(0); int topY = SoilMap.GetLength(1); int area = topX * topY; for (int x = 0; x < topX; x++) { for (int y = 0; y < topY; y++) { float localSoil = SoilMap[x, y] - RockMap[x, y]; float localWater = WaterMap[x, y] - SoilMap[x, y]; float localHumidity = HumidityMap[x, y]; float localHeight = SoilMap[x, y]; if (localSoil < 0) localSoil = 0; if (localWater < 0) localWater = 0; List<float> localInclines = GetLocalInclines(x, y); soilMass += localSoil; waterMass += localWater; humidityMass += localHumidity; totalHeight += localHeight; if (localSoil > highSoil ) highSoil = localSoil; if (localWater > highWater ) highWater = localWater; if (localHumidity > highHumidity ) highHumidity = localHumidity; if (localHeight > highHeight ) highHeight = localHeight; if (localSoil < lowSoil || lowSoil == 0) lowSoil = localSoil; if (localWater < lowWater || lowWater == 0) lowWater = localWater; if (localHumidity < lowHumidity || lowHumidity == 0) lowHumidity = localHumidity; if (localHeight < lowHeight || lowHeight == 0) lowHeight = localHeight; foreach (float incline in localInclines) { totalIncline += incline; inclineCount++; if (incline > highIncline) highIncline = incline; if (incline < lowIncline) lowIncline = incline; } surfaceCounter[SurfaceMap[x, y]]++; } } avgSoil = soilMass / area; avgWater = waterMass / area; avgHumidity = humidityMass / area; avgHeight = totalHeight / area; avgIncline = totalIncline / inclineCount; Output[(int)Stats.SoilMass ] = soilMass .ToString(); Output[(int)Stats.WaterMass ] = waterMass .ToString(); Output[(int)Stats.HumidityMass ] = humidityMass.ToString(); Output[(int)Stats.HighSoil ] = highSoil .ToString(); Output[(int)Stats.HighWater ] = highWater .ToString(); Output[(int)Stats.HighHumidity ] = highHumidity.ToString(); Output[(int)Stats.HighHeight ] = highHeight .ToString(); Output[(int)Stats.HighIncline ] = highIncline .ToString(); Output[(int)Stats.LowSoil ] = lowSoil .ToString(); Output[(int)Stats.LowWater ] = lowWater .ToString(); Output[(int)Stats.LowHumidity ] = lowHumidity .ToString(); Output[(int)Stats.LowHeight ] = lowHeight .ToString(); Output[(int)Stats.LowIncline ] = lowIncline .ToString(); Output[(int)Stats.AvgSoil ] = avgSoil .ToString(); Output[(int)Stats.AvgWater ] = avgWater .ToString(); Output[(int)Stats.AvgHumidity ] = avgHumidity .ToString(); Output[(int)Stats.AvgHeight ] = avgHeight .ToString(); Output[(int)Stats.AvgIncline ] = avgIncline .ToString(); Output[(int)Stats.MostSurface ] = GetMostSurface(surfaceCounter); }
private void DoWaterFlow() { int soilType = (int)SurfaceType.Soil; // Loop geral do mapa for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { // Altura do terreno + altura da água float localSurfaceHeight = WaterMap[x, y]; float localWaterVolume = localSurfaceHeight - SoilMap[x, y]; if (localWaterVolume <= 0) { continue; } // Média das alturas float avgSurfaceHeight = 0; int countHeights = 0; // Soma das diferenças de altura positivas float totalDifference = 0; // Loop horizontal VonNeumannTransform(x, y, SoilMap, (ref float localHeight, ref float nearbyHeight, int nearbyX, int nearbyY) => { float nearbySurfaceHeight = WaterMap[nearbyX, nearbyY]; float difference = localSurfaceHeight - nearbySurfaceHeight; if (difference < 0) { return; } totalDifference += difference; avgSurfaceHeight += nearbySurfaceHeight; countHeights++; } ); // Se não houver diferenças positivas prosseguir if (totalDifference == 0) { continue; } avgSurfaceHeight /= countHeights; float deltaSurfaceHeight = localSurfaceHeight - avgSurfaceHeight; float totalDeltaWater = 0; // Loop horizontal VonNeumannTransform(x, y, SoilMap, (ref float localHeight, ref float nearbyHeight, int nearbyX, int nearbyY) => { float nearbySurfaceHeight = WaterMap[nearbyX, nearbyY]; if (nearbySurfaceHeight >= localSurfaceHeight) { return; } float difference = localSurfaceHeight - nearbySurfaceHeight; float deltaWater = Math.Min(localWaterVolume, deltaSurfaceHeight) * (difference / totalDifference); WaterMap[nearbyX, nearbyY] += deltaWater; totalDeltaWater += deltaWater; // Quando o nível da água passar de um certo ponto, destruir a superfície local if (WaterMap[nearbyX, nearbyY] - SoilMap[nearbyX, nearbyY] > 0.25f) { SurfaceMap[nearbyX, nearbyY] = soilType; } } ); if (totalDeltaWater > 0) { WaterMap[x, y] -= totalDeltaWater; if (WaterMap[x, y] < SoilMap[x, y]) { WaterMap[x, y] = SoilMap[x, y]; } UpdateMeshes = true; UpdateShades = true; } } } }
public void ApplyVonNeumann() { Directions direction = Configs.WindDirection; int topX = SoilMap.GetLength(0); int topY = SoilMap.GetLength(1); float[,] baseHeights = SoilMap.Clone() as float[, ]; for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { float sumHeights = 0.0f; int countHeights = 0; // Direções horizontais if (direction == Directions.East || direction == Directions.West) { int from = direction == Directions.East ? -Configs.Range : 0; int to = direction == Directions.East ? 0 : Configs.Range; // Todos os vizinhos verticais for (int relX = -Configs.Range; relX <= Configs.Range; relX++) { int absX = x + relX; if (absX < 0 || absX >= topX) { continue; } sumHeights += baseHeights[absX, y]; countHeights++; } // Metade dos vizinhos horizontais for (int relY = from; relY <= to; relY++) { int absY = y + relY; if (absY < 0 || absY >= topY) { continue; } sumHeights += baseHeights[x, absY]; countHeights++; } // Subtrair o valor da célula central que foi somado duas vezes sumHeights -= baseHeights[x, y]; countHeights--; } // Direções verticais if (direction == Directions.North || direction == Directions.South) { int from = direction == Directions.North ? -Configs.Range : 0; int to = direction == Directions.North ? 0 : Configs.Range; // Metade dos vizinhos verticais for (int relX = from; relX <= to; relX++) { int absX = x + relX; if (absX < 0 || absX >= topX) { continue; } sumHeights += baseHeights[absX, y]; countHeights++; } // Todos os vizinhos horizontais for (int relY = -Configs.Range; relY <= Configs.Range; relY++) { int absY = y + relY; if (absY < 0 || absY >= topY) { continue; } sumHeights += baseHeights[x, absY]; countHeights++; } // Subtrair o valor da célula central que foi somado duas vezes sumHeights -= baseHeights[x, y]; countHeights--; } // Direções diagonais else { int fromX = 0; int fromY = 0; int toX = 0; int toY = 0; // Selecionar valores para representar a diagonal selecionada switch (direction) { case Directions.Northeast: fromX = -Configs.Range; fromY = -Configs.Range; break; case Directions.Southeast: toX = Configs.Range; fromY = -Configs.Range; break; case Directions.Southwest: toX = Configs.Range; toY = Configs.Range; break; case Directions.Northwest: fromX = -Configs.Range; toY = Configs.Range; break; } // Vizinhos verticais for (int relX = fromX; relX <= toX; relX++) { int absX = x + relX; if (absX < 0 || absX >= topX) { continue; } sumHeights += baseHeights[absX, y]; countHeights++; } // Vizinhos horizontais for (int relY = fromY; relY <= toY; relY++) { int absY = y + relY; if (absY < 0 || absY >= topY) { continue; } sumHeights += baseHeights[x, absY]; countHeights++; } // Subtrair o valor da célula central que foi somado duas vezes sumHeights -= baseHeights[x, y]; countHeights--; } // Aplicar média if (countHeights > 0) { float avg = sumHeights / countHeights; if (avg < SoilMap[x, y]) { float diff = avg - SoilMap[x, y]; SoilMap[x, y] += diff * Configs.Factor; } } } } }
public override void ApplyTransform() { int topX = SoilMap.GetLength(0); int topY = SoilMap.GetLength(1); float[,] baseHeights = SoilMap.Clone() as float[, ]; float[,] heightDiff = new float[topX, topY]; for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { // Fazer a média da altura com base nas alturas vizinhas // Apenas considerar variação para baixo float sumHeights = 0.0f; int countHeights = 0; for (int relX = -1; relX <= 1; relX++) { int absX = x + relX; if (absX < 0 || absX >= topX) { break; } for (int relY = -1; relY <= 1; relY++) { int absY = y + relY; if (absY < 0 || absY >= topY) { break; } sumHeights += baseHeights[absX, absY]; countHeights++; } } if (countHeights > 0) { float avg = sumHeights / countHeights; if (avg < SoilMap[x, y]) { SoilMap[x, y] = avg; heightDiff[x, y] = baseHeights[x, y] - avg; } } } } // Distribuir a massa de terra removida para os terrenos mais baixos for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { int countLowLands = 0; // Quantidade de nodos mais baixos for (int relX = -1; relX <= 1; relX++) { int absX = x + relX; if (absX < 0 || absX >= topX) { break; } for (int relY = -1; relY <= 1; relY++) { int absY = y + relY; if (absY < 0 || absY >= topY) { break; } if (absX != x && absY != y && SoilMap[absX, absY] <= SoilMap[x, y]) { countLowLands++; } } } // Distribuição if (countLowLands > 0) { float depositPerPlot = heightDiff[x, y] / countLowLands; for (int relX = -1; relX <= 1; relX++) { int absX = x + relX; if (absX < 0 || absX >= topX) { break; } for (int relY = -1; relY <= 1; relY++) { int absY = y + relY; if (absY < 0 || absY >= topY) { break; } if (absX != x && absY != y && SoilMap[absX, absY] <= SoilMap[x, y]) { SoilMap[absX, absY] += depositPerPlot; } } } } else { SoilMap[x, y] += heightDiff[x, y]; } } } }
public void DoTransform() { // Loop geral do mapa for (int x = 0; x < SoilMap.GetLength(0); x++) { for (int y = 0; y < SoilMap.GetLength(1); y++) { // Concreto não pode sofrer erosão, mas pode ser destruído por queda de materiais. if (SurfaceMap[x, y] == (int)SurfaceType.Concrete) { continue; } float localSoilMass = SoilMap[x, y] - RockMap[x, y]; // Obter a maior inclinação, a soma das inclinações superiores à configuração e a quantidade esperada de material movido float maxInclination = 0.0f; float sumInclinations = 0.0f; float sumMovedMaterial = 0.0f; float thresholdInclination = Configs.MaxInclination * SurfaceInclinationModifiers[SurfaceMap[x, y]]; // Inclinação máxima reduzida até a metade de acordo com a umidade do local thresholdInclination -= (HumidityMap[x, y] * thresholdInclination) / 2; VonNeumannTransform(x, y, SoilMap, (ref float localHeight, ref float nearbyHeight, int nearbyX, int nearbyY) => { float inclination = localHeight - SoilMap[nearbyX, nearbyY]; if (inclination > maxInclination) { maxInclination = inclination; } if (inclination > thresholdInclination) { sumInclinations += inclination; sumMovedMaterial += Configs.DistributionFactor * (inclination - thresholdInclination); } } ); // Se não houver nada para alterar prosseguir imediatamente if (sumInclinations == 0.0) { continue; } // Limitar a quantidade de material movimentada se não houver solo o bastante float movementLimitingFactor = 1.0f; if (sumMovedMaterial > localSoilMass) { movementLimitingFactor = localSoilMass / sumMovedMaterial; } // Mover material para os vizinhos com inclinações superiores à configuração sumMovedMaterial = 0.0f; // Constante para os próximos cálculos float inclinationDifference = (maxInclination - thresholdInclination); int soilIndex = (int)SurfaceType.Soil; VonNeumannTransform(x, y, SoilMap, (ref float localHeight, ref float nearbyHeight, int nearbyX, int nearbyY) => { float inclination = localHeight - SoilMap[nearbyX, nearbyY]; if (inclination > thresholdInclination) { float movedMaterial = Configs.DistributionFactor * inclinationDifference * (inclination / sumInclinations); movedMaterial *= movementLimitingFactor; SoilMap[nearbyX, nearbyY] += movedMaterial; WaterMap[nearbyX, nearbyY] += movedMaterial; sumMovedMaterial += movedMaterial; if (SurfaceMap[nearbyX, nearbyY] != soilIndex) { // Locais que recebem queda de material têm sua superfície destruída SurfaceMap[nearbyX, nearbyY] = soilIndex; UpdateTextures = true; } } } ); if (sumMovedMaterial > 0) { SoilMap[x, y] -= sumMovedMaterial; WaterMap[x, y] -= sumMovedMaterial; UpdateMeshes = true; UpdateShades = true; if (SurfaceMap[x, y] != soilIndex) { SurfaceMap[x, y] = soilIndex; UpdateTextures = true; } } } } }