Export Heightmap by Code?

how to export heightmap of a Terrain terrain; as png?

Heightmaps aren’t pngs, but are raw files. You’d go through the heightmap data and output it as a series of unsigned 16-bit integers.

–Eric

Just a little guidance on how to get started and I’ll be able to make a start I have this so far

using UnityEngine;
using System.Collections;
using System.IO;

public class HeightDetail_Manager : MonoBehaviour {
    public Terrain terrain;
    public static HeightDetail_Manager getDetailManager(){
        return FindObjectOfType<HeightDetail_Manager> ();
    }
    // Use this for initialization
    public void init () {
        int myIndex = 0;
        Texture2D duplicateHeightMap = new Texture2D(terrain.terrainData.heightmapWidth, terrain.terrainData.heightmapHeight, TextureFormat.ARGB32, false);
        float[,] rawHeights = terrain.terrainData.GetHeights(0, 0, terrain.terrainData.heightmapWidth, terrain.terrainData.heightmapHeight);
        /// run through the array row by row
        for (int y=0; y < duplicateHeightMap.height; ++y)
        {
            for (int x=0; x < duplicateHeightMap.width; ++x)
            {
                /// for wach pixel set RGB to the same so it's gray
                Color color = new Color(rawHeights[x,y], rawHeights[x,y], rawHeights[x,y], 1.0f);
                duplicateHeightMap.SetPixel (x, y, color);
                myIndex++;
            }
        }
        // Apply all SetPixel calls
        duplicateHeightMap.Apply();
       
        /// make it a PNG and save it to the Assets folder
        byte[] myBytes = duplicateHeightMap.EncodeToPNG();
        string filename = "DupeHeightMap.png";
        File.WriteAllBytes(Application.dataPath + "/Temp/" + filename, myBytes);
    }
   
    // Update is called once per frame
    void Update () {
   
    }

    void Start(){
        init ();
    }
}

As you say it isn’t png how about this?

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();
    }

No, raw files don’t use floats. Unsigned 16-bit int, as I said.

–Eric

Right okay but how is that going to look for a method that returns float[,]?

Convert the floats to unsigned 16-bit ints.

–Eric

Could you provide some documentation on unsigned 16-bit ints and how I’d convert one float?
is it the same as a normal int?

No. UInt16 Struct (System) | Microsoft Learn

–Eric

void SaveTerrain(string filename) {
        float[,] dat = terrain.GetHeights(0,0,terrain.heightmapWidth,terrain.heightmapHeight);
        short[,] dat2 = new short[dat.Length, dat.Length];
        for(int i = 0; i < dat.Length; i++) {
            for(int j = 0; j < dat.Length; j++) {
                dat2[i,j] = System.Convert.ToInt16(dat[i,j]);
            }
        }

        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(dat2[i,j]);
            }
        }
        bw.Close();
    }

This is where I am,
getting an out of memory exception on dat2 on the second line

You use GetLength(0) and GetLength(1) for the appropriate lengths of 2D arrays, not Length. Using System.Convert.ToInt16 won’t work because you end up with 0 or 1 (mostly 0). The input is 0.0 to 1.0; the output should be 0 to 65535.

–Eric

For anyone interested this works

IEnumerator SaveTerrain(string filename) {
        float[,] dat = terrain.GetHeights(0,0,terrain.heightmapWidth,terrain.heightmapHeight);
        ushort[,] dat2 = new ushort[dat.GetLength (0), dat.GetLength (0)];
        for(int i = 0; i < dat.GetLength (0); i++) {
            for(int j = 0; j < dat.GetLength (0); j++) {
                ushort sh = System.Convert.ToUInt16(dat[i,j]*65535);
                dat2[i,j] = sh;
            }
            Debug.Log (((float)i/(float)dat.GetLength(0)) * 100 + "%");
            yield return null;
        }

        FileStream fs = new FileStream("Assets/Temp/" + filename + ".raw", 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(dat2[i,j]);
            }
            yield return null;
        }
        bw.Close();
    }

I would take out the “yield return null” lines; that’s just going to make the process take an unnecessarily long time.

–Eric