Hello!
Now we’re on 2019.2.0b7, and I’m using the following (note I’m not using render textures, I’m doing everything using the jobs system, and writing to the texture using Texture2D.GetRawTextureData and writing to that in the jobs. This is primarily because I am not good at shader coding though more than anything else):
// the Vector2Int here is an index into the terrain patch
List<(HeightCalculationJob, JobHandle, Vector2Int)> calcJobs = new List<(HeightCalculationJob, JobHandle, Vector2Int)>();
foreach (var heightJob in calcJobs)
{
int tx = heightJob.Item3.x;
int ty = heightJob.Item3.y;
var dirtyRect = forDirtyRects[tx, ty];
heightJob.Item2.Complete();
forHeightTextures[tx, ty].Apply();
Graphics.CopyTexture(forHeightTextures[tx, ty], forTerrainComps[tx, ty].terrainData.heightmapTexture);
var dirtySize = dirtyRect.Size;
forTerrainComps[tx, ty].terrainData.DirtyHeightmapRegion(new RectInt(dirtyRect.min.x, dirtyRect.min.y, dirtySize.x, dirtySize.y), TerrainHeightmapSyncControl.None);
var job = heightJob.Item1;
job.GetRemainingDirtyResults(ref forDirtyRects[tx, ty]);
if (wasDetailDataChanged || !forDirtyRects[tx, ty].isDirty)
{
forTerrainComps[tx, ty].terrainData.SyncHeightmap();
}
}
So each job calculates the heightmap that I want to set and puts it on a Texture (forHeightTextures). I then copy that to the terrain component’s heightmapTexture, then mark the region within the heightmap that I was actually working on dirty, passing in None to syncControl. This is all super fast and allows me to interpolate heights over time to create a smooth transition between terrain types - you can see it in action here.
Then, I check if the interpolation has completed, and only then do I call SyncHeightmap. This is the expensive part. It causes the phys geo to be updated, and also the grass(detail) billboards that are on that part of the heightmap get set to the correct height. This causes a pop, which I’d rather avoid. But without it it just runs too slow for our needs.
Today I tried passing in TerrainHeightmapSyncControl.TerrainAndLOD to DirtyHeightmapRegion, and this then updates the phys geo & grass sprites immediately, but is expensive again. I’m not totally sure what the difference betwen doing that and calling SyncHeightmap is, but I guess SyncHeightmap syncs the whole thing, rather than just the dirty region passed in to DirtyHeightmapRegion. But not totally sure - I need to profile it. But just changing that over appears to be still too slow for our game (we have some effects that can cause a number of changes to the heightmap over time, which needs to be buttery smooth. So we’re living with the pops for now.
It would be great though if there was a mode that could be passed in to DirtyHeightmapRegion which only did the necessary calculations for the grass billboards to be correctly positioned every frame, without doing the phys/cpu/LOD update, which I don’t care about so much. Would that be a possibility, @wyatttt ?
It would be nice to have a direct API for the terrain that took NativeArrays, though having this texture-based API instead is not so bad, as the texture API itself can take NativeArrays. And the bottleneck appears to be the sync step anyway.