Loading terrain heightmaps at runtime?

Is it possible to load heightmap data into a terrain at runtime in Unity?
If so, how might I go about doing this?
Thanks.

Hi, welcome to the forum!

You can use the TerrainData class’s SetHeights function to set the heights of the terrain’s sample points. However, it may be more complicated than this depending on what format the original height data is in.

Thanks for the reply, that helped.
After doing some digging around, it seems that heightmap data is stored in a two dimensional array of floats. You can take a swath of terrain, read and set its height by using TerrainData.GetHeights and TerrainData.SetHeights. I’ve got a very basic manipulation of the terrain working, and a save/load feature using TerrainData.heightmapWidth and TerrainData.heightmapHeight to get/set the heights of the entire map. It looks like I can manipulate the texture splatting and detail meshes using the TerrainData.detailPrototypes and TerrainData.splatPrototypes properties as well. I’ll be experimenting with those soon. Hopefully this will do everything I need.

For anyone interested, here’s my save/load functions:

	void SaveTerrain(string filename) {
		float[,] dat = terrain.GetHeights(0,0,terrain.heightmapWidth,terrain.heightmapHeight);
		FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite);
		BinaryWriter bw = new BinaryWriter(fs);
		for(int i = 0; i < terrain.heightmapWidth; i++) {
			for(int j = 0; j < terrain.heightmapHeight; j++) {
				bw.Write(dat[i,j]);
			}
		}
		bw.Close();
	}
	
	void LoadTerrain(string filename) {
		float[,] dat = terrain.GetHeights(0,0,terrain.heightmapWidth,terrain.heightmapHeight);
		FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite);
		BinaryReader br = new BinaryReader(fs);
		br.BaseStream.Seek(0, SeekOrigin.Begin);
		for(int i = 0; i < terrain.heightmapWidth; i++) {
			for(int j = 0; j < terrain.heightmapHeight; j++) {
				dat[i,j] = (float)br.ReadSingle();
			}
		}
		br.Close();
		terrain.SetHeights(0,0,dat);
		heights = terrain.GetHeights(50,50,100,100);
	}

The terrain variable is a public TerrainData which I assign via the inspector. Heights is a local variable I use for terrain manipulation, shown in the load function so I don’t get jumps when I try to change the terrain after loading. I hope to get a cleaner implimentation soon, but this is working so far!

anymore progress on this?

I’ve tried your code and all I get is a

EndOfStreamException: Failed to read past end of stream.
System.IO.BinaryReader.FillBuffer (Int32 numBytes)
System.IO.BinaryReader.ReadSingle ()
TerrainManager.LoadTerrain (System.String filename) (at Assets/MyScripts/c#/Managers/TerrainManager.cs:37)
TerrainManager.Start () (at Assets/MyScripts/c#/Managers/TerrainManager.cs:

The following is a Terrain Manager Class … your code only raped … connected to a game object with and existing terrain and default heightmap loaded. All height maps were done in gimp converted to gray scale and scaled to the same size and exported as .raw. I call LoadTerrain in Start but see only the error … no changes to the map :frowning:

using UnityEngine;
using System.Collections;
using System.IO;
public class TerrainManager : MonoBehaviour {

	public TerrainData terrain;
	float[,] heights;
	// Use this for initialization
	void Start () {
		LoadTerrain("assets/textures/planets/intersteller/rocky/heightmaps/rocky4.raw");
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	
    void SaveTerrain(string filename) {
        float[,] dat = terrain.GetHeights(0,0,terrain.heightmapWidth,terrain.heightmapHeight);
        FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.ReadWrite);
        BinaryWriter bw = new BinaryWriter(fs);
        for(int i = 0; i < terrain.heightmapWidth; i++) {
            for(int j = 0; j < terrain.heightmapHeight; j++) {
                bw.Write(dat[i,j]);
            }
        }
        bw.Close();
    }
    
    void LoadTerrain(string filename) {
        float[,] dat = terrain.GetHeights(0,0,terrain.heightmapWidth,terrain.heightmapHeight);
        FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.ReadWrite);
        BinaryReader br = new BinaryReader(fs);
        br.BaseStream.Seek(0, SeekOrigin.Begin);
        for(int i = 0; i < terrain.heightmapWidth; i++) {
            for(int j = 0; j < terrain.heightmapHeight; j++) {
                dat[i,j] = (float)br.ReadSingle();
            }
        }
        br.Close();
        terrain.SetHeights(0,0,dat);
        heights = terrain.GetHeights(50,50,1024,1024);
    }
}