I have multiple unity terrains next to each other that i want to seamlessly erode. my current problem is how to set heights across terrain borders. The current approach is to set single heightsamples when the SetHeightsDelayLOD array would go out of bounds. Basically i provide SetHeightsDelayLOD with a [1,1] array with a single heightsample. This is expensive so I’m having it set larger arrays at a time when not crossing borders. Sadly these larger arrays seem to do nothing, event though they use the same exact function only with a larger array. Any other suggestions to improve this code are welcome, I’m still learning.
Here is the function that sets the heights. pos is the coordinates for the middle of the array. yMod and xMod is used to offset this to either access neighbouring samples or find the base coords of the array.
public void setHeight(Vector2 pos, float[,] heights, int yMod = 0, int xMod = 0)
{
pos = new Vector2(pos.x + xMod, pos.y + yMod);
Vector2 chunkPos = TerrainSettings.worldToChunk(pos);
Vector2 heightPos = TerrainSettings.worldToHeightMap(pos);
chunks[(int)chunkPos.x, (int)chunkPos.y].terrain.terrainData.SetHeightsDelayLOD((int)heightPos.x,
(int)heightPos.y, heights);
if(!chunksToUpdate.Contains(chunks[(int)chunkPos.x, (int)chunkPos.y]))
chunksToUpdate.Enqueue(chunks[(int)chunkPos.x, (int)chunkPos.y]);
}
Here is the function calculating mass to erode which then calls setHeights. It works fine when sending single samples, but when i try the larger array no changes are visible.
public static Drop takeSediment(Map map, Drop drop, float heightDif)
//calculates the amount of mass to take from the surrounding area
{
float[,] array = new float[radius * 2 + 1, radius * 2 +1];
float[,] heights = new float [2,2];
bool multiChunk = false;
//Debug.Log("takes " + Mathf.Min((drop.capacity - drop.sediment) *
erosion, heightDif * -1));
float takeAmount = Mathf.Min((drop.capacity - drop.sediment) *
erosion, (heightDif * -1)*radius);
drop.sediment += takeAmount;
float totalWeight = 0;
for (int x = -radius; x <= radius; x++)
{
for (int y = -radius; y <= radius; y++)
{
array[x + radius, y + radius] = Mathf.Max(0, radius -
(Vector2.Distance(drop.posOld, new
Vector2(drop.posOld.x + x, drop.posOld.y + y))));
totalWeight += Mathf.Max(0, radius -
(Vector2.Distance(drop.posOld, new
Vector2(drop.posOld.x + x, drop.posOld.y + y))));
}
}
for (int x = -radius; x <= radius; x++)
{
for (int y = -radius; y <= radius; y++)
{
if(TerrainSettings.worldToHeightMap(drop.posOld).x
+ radius > TerrainSettings.HeightmapResolution
|| TerrainSettings.worldToHeightMap(drop.posOld).y
+ radius >
TerrainSettings.HeightmapResolution
|| TerrainSettings.worldToHeightMap(drop.posOld).x
- radius < 0
|| TerrainSettings.worldToHeightMap(drop.posOld).y
- radius < 0)
{
//if it intersects the border use single samples
float test = map.getHeight(drop.posOld, y, x);
multiChunk = true;
float[,] tempArray = new float[1, 1] { {
map.getHeight(drop.posOld, y, x) - ((array[x + radius,
y + radius] / totalWeight) * takeAmount) } };
map.setHeight(drop.posOld, tempArray, y, x);
}
else
{
//does not intersect border, send whole array at once
multiChunk = false;
heights = map.getHeights(drop.posOld, radius);
float test = heights[x + radius, y + radius];
heights[x + radius, y + radius] =
heights[x + radius, y + radius] -
((array[x + radius, y + radius] / totalWeight) * takeAmount);
}
}
}
if (!multiChunk)
{
//this is the call that doesn't do anything
map.setHeight(drop.posOld, heights, -radius, -radius);
}
return drop;
}