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