SetAlphamaps with multiple terrains at runtime

I’m trying to use the SetAlphamaps function on multiple terrains at run-time, but I’m running into an issue. Here’s a rundown of what I have;

I’m generating 4 terrain tiles at run-time, and generating heightmaps via perlin noise. After this I am setting the splatmaps based off of height and slope.

This all works perfectly fine when I do it for one terrain. Now that I am using multiple terrain tiles, when I use SetAlphamaps it sets the the alphamap for all the terrains to the same one. It seems as if the terrainData is static or something.

Here is a relevant code snippet:

void genTextures(TerrainData terrain)
	{
		int xRes = terrain.heightmapHeight;
		int zRes = terrain.heightmapHeight;
		float[,] heights = terrain.GetHeights(0, 0, xRes, zRes);

		float[, ,] splatmapData = new float[terrain.alphamapWidth, terrain.alphamapHeight, terrain.alphamapLayers];
		Vector4 peak = new Vector4 (1, 0, 0, 0);
		Vector4 grass = new Vector4 (0, 1, 0, 0);
		Vector4 dirt = new Vector4 (0, 0, 1, 0);
		Vector4 cliff = new Vector4 (0, 0, 0, 1);
		Vector4 splat = dirt;

		int maxHeight = 600;
		int layer1 = 250;
		int layer2 = 175;
		for (int i = 0; i < terrain.alphamapHeight; i++)
		{
			for (int j = 0; j < terrain.alphamapWidth; j++)
			{
				Vector2 normalizedPos = new Vector2(Mathf.InverseLerp(0.0f, terrain.alphamapHeight, j), Mathf.InverseLerp(0.0f, terrain.alphamapWidth, i));
				Vector3 angle = terrain.GetInterpolatedNormal(normalizedPos.x, normalizedPos.y);
				float cliffAngle = Vector3.Angle(Vector3.up, angle);
				float slope1 = 67f;
				float slope2 = 63f;
				if(heights[i, j] > layer1)
				{
					Vector3 temp = Vector4.Lerp (grass, peak, (heights[i, j] - layer1) / (maxHeight - layer1));
					if(cliffAngle > slope2)
					{
						splat = Vector4.Lerp (temp, cliff, (cliffAngle - slope2) / (3 * (slope1 - slope2)));
					}
					else
					{
						splat = temp;
					}
				}
				else if(heights[i, j] > layer2)
				{
					Vector3 temp = Vector4.Lerp (dirt, grass, (heights[i, j] - layer2) / (layer1 - layer2));
					if(cliffAngle > slope2)
					{
						splat = Vector4.Lerp (temp, cliff, (cliffAngle - slope2) / (3 * (slope1 - slope2)));
					}
					else
					{
						splat = temp;
					}
				}
				else
				{
					if(cliffAngle > slope2)
					{
						splat = Vector4.Lerp (dirt, cliff, (cliffAngle - slope2) / (3 * (slope1 - slope2)));
					}
					else
					{
						splat = Vector4.Lerp (dirt, dirt, (heights[i, j] - layer1) / (maxHeight - layer1));
					}
				}
				
				splat.Normalize();
				splatmapData [i, j, 0] = splat.x;
				splatmapData [i, j, 1] = splat.y;
				splatmapData [i, j, 2] = splat.z;
				splatmapData [i, j, 3] = splat.w;
			}
		}
		terrain.SetAlphamaps(0, 0, splatmapData);
	}

This function is called in a for loop that cycles through each terrain object, passing in the relevant TerrainData object. This is exactly the same way I set my heightmap for each Terrain, so I’m confused as to why the alphamaps would not work the same.

Any help is appreciated. If I need to explain further, please let me know. Here is a screenshot showing that the alphamap is the same across 4 terrain tiles.

EDIT

Ok, as per my last comment, here is some additional code that may be involved.

TerrainData terdata;
		terdata = Resources.Load("template") as TerrainData;
		int terRes = 2000;
		int terrainArraySize = 2;
		for(int i = 0; i < terrainArraySize; i++)
		{
			for(int j = 0; j < terrainArraySize; j++)
			{	
				file = file + (j + (i * terrainArraySize));
				terrainsdataArray[i, j] = (TerrainData) Object.Instantiate(terdata);
				terrainArray[i, j] = Terrain.CreateTerrainGameObject(terrainsdataArray[i, j]);
				terrainArray[i, j].transform.Translate((((j) * terRes) + terrainArray[i, j].transform.position.x),
													  (terrainArray[i,j].transform.position.y),
													  (((i) * terRes) + terrainArray[i, j].transform.position.z),
													  Space.World);
				terrainArray[i, j].GetComponent<Terrain>().basemapDistance = 1000;
				genTerrainHeightmap(terrainsdataArray[i,j], j, i);
			}
		}

Basically I have an empty terrain asset saved in the resources folder. I load that in (‘template’) and then go through an a nested for loop, instantiating copies of it that I then change at runtime. It appears to me that the problem is with the saved terrain. A child of that asset is called SplatAlpha0. This is an image with RGBA channels. I’m pretty sure that this is where the splats are saved, and helps to make the baseMap. The issue I believe is that when I use the SetAlphamaps function, it is writing to this one file, but all the terrain objects use this file.

My idea is to create multiple image files, and somehow tell the terrain to use these files rather than the default. If this is not possible, or if there is a better idea someone has, please let me know. The end goal is to make endless terrain, but for initial release, I may need to limit the number of terrain tiles.

I just faced this same problem, and while this question was asked long ago, I thought I’d post how I solved it here in case anyone else runs into the same problem.

Instantiating a new TerrainData wasn’t enough, in my case. Doing that allowed me to set different height values for the different domains, but they still all used the same splat maps. To have them use different splat maps, I had to create an individual SplatPrototypes array for each individual TerrainData, and fill it with the SplatPrototype textures. Here’s the code that I wrote to do this:

			terrainPatches[x,z] = (Terrain) Instantiate(terrainPrefab, new Vector3(0, 0, 0), Quaternion.identity);
			
			terrainPatches[x,z].terrainData = new TerrainData();
			terrainPatches[x,z].terrainData.heightmapResolution = 129;
			terrainPatches[x,z].terrainData.size = new Vector3(200,80,200);
			terrainPatches[x,z].terrainData.alphamapResolution = 128;
			terrainPatches[x,z].terrainData.baseMapResolution = 256;
			terrainPatches[x,z].terrainData.SetDetailResolution(256, 8);
			SplatPrototype[] splatPrototypes = new SplatPrototype[10];
			for (int i = 0; i < 10; i++) {
				splatPrototypes *= new SplatPrototype();*

_ splatPrototypes*.tileOffset = new Vector2(0,0);_
_ splatPrototypes.tileSize = new Vector2(20,20);
switch (i) {
case 0: splatPrototypes.texture = terrainTexture0; splatPrototypes.normalMap = rockNormalMap; break;
case 1: splatPrototypes.texture = terrainTexture1; break;
case 2: splatPrototypes.texture = terrainTexture2; break;
case 3: splatPrototypes.texture = terrainTexture3; splatPrototypes.normalMap = grassNormalMap; break;
case 4: splatPrototypes.texture = terrainTexture4; splatPrototypes.normalMap = rockNormalMap; break;
case 5: splatPrototypes.texture = terrainTexture5; splatPrototypes.normalMap = rockNormalMap; break;
case 6: splatPrototypes.texture = terrainTexture6; break;
case 7: splatPrototypes.texture = terrainTexture7; break;
case 8: splatPrototypes.texture = terrainTexture8; break;
case 9: splatPrototypes.texture = terrainTexture9; break;
}
}
terrainPatches[x,z].terrainData.splatPrototypes = splatPrototypes;*_