Yes, I know the terrain API is largely unsupported, etc.
Anyways - the script below crashes Unity. If anyone can say what I’m doing wrong, any help would be appreciated:
static var Threshold = 0.8;
static var Variation = 0.1;
static var Step = 10;
@MenuItem ("Terrain/Splat Trees")
static function SplatTrees () {
var splatmap : Texture2D = Selection.activeObject as Texture2D;
if (splatmap == null) {
EditorUtility.DisplayDialog("No texture selected", "Please select a texture", "Cancel");
return;
}
if (splatmap.format != TextureFormat.ARGB32) {
EditorUtility.DisplayDialog("Wrong format", "Splatmap must be in RGBA 32 bit format", "Cancel");
return;
}
var w = splatmap.width;
if (splatmap.height != w) {
EditorUtility.DisplayDialog("Wrong size", "Splatmap width and height must be the same", "Cancel");
return;
}
if (Mathf.ClosestPowerOfTwo(w) != w) {
EditorUtility.DisplayDialog("Wrong size", "Splatmap width and height must be a power of two", "Cancel");
return;
}
var terrain = Terrain.activeTerrain.terrainData;
var mapColors = splatmap.GetPixels();
var index = -1; var trees = 0;
var instances = new ArrayList();
for (y = 0; y < w; y+=Step) {
for (x = 0; x < w; x+=Step) {
// place the chosen tree, if the colours are right
index = -1;
var col = mapColors[((w-1)-x)*w + y];
if (col.r>Threshold) {
index = 0;
} else if (col.g>Threshold) {
index = 1;
} else if (col.b>Threshold) {
index = 2;
}
if (index>=0) {
// place a tree
trees++;
var instance = new TreeInstance();
instance.position = Vector3(x,y,0);
instance.color = Color.white;
instance.lightmapColor = Color.white;
instance.prototypeIndex = index;
instance.widthScale = 1.0 + Random.Range(-Variation,Variation);
instance.heightScale = 1.0 + Random.Range(-Variation,Variation);
instances.Add(instance);
}
}
}
print("placed "+trees+" trees");
terrain.treeInstances = instances.ToArray(typeof(TreeInstance));
// terrain.RecalculateTreePositions();
}
What this would do, if it wouldn’t crash, is take a splatmap and automatically paint trees on the terrain based on said map. Nifty, uh? I’d love to use this for my autogenerated terrains (see in Showcases), both for trees and grass.
But, alas, no cigar.
The crash happens at the last line, the "terrain.treeInstances = " part. If I comment that out, everything else works just fine.
And no, it can’t be the number of trees, in my test runs I’d place less than 1000.
in C# there is such functionality offered from the system side to convert but at the end of the day its always a “convert each object and add it to an array of the right type”
using UnityEngine;
using UnityEditor;
using System.Collections;
public class ApplyTreeMap
{
[MenuItem("Terrain/Apply TreeMap")]
static void Do () {
// hardcoded config
float Threshold = 0.8f;
float Variation = 0.1f;
int Step = 4;
// set up my data
TerrainData data = Terrain.activeTerrain.terrainData;
Texture2D splatmap = (Texture2D)Selection.activeObject;
if (splatmap == null) {
EditorUtility.DisplayDialog("No texture selected", "Please select a texture", "Cancel");
return;
}
if (splatmap.format != TextureFormat.ARGB32) {
EditorUtility.DisplayDialog("Wrong format", "Splatmap must be in RGBA 32 bit format", "Cancel");
return;
}
int w = splatmap.width;
if (splatmap.height != w) {
EditorUtility.DisplayDialog("Wrong size", "Splatmap width and height must be the same", "Cancel");
return;
}
if (Mathf.ClosestPowerOfTwo(w) != w) {
EditorUtility.DisplayDialog("Wrong size", "Splatmap width and height must be a power of two", "Cancel");
return;
}
Vector3 terrainSize = data.size;
Vector3 terrainPos = Terrain.activeTerrain.transform.position;
Color[] mapColors = splatmap.GetPixels();
int index = -1;
int trees = 0;
ArrayList instances = new ArrayList();
for (int y = 0; y < w; y+=Step) {
for (int x = 0; x < w; x+=Step) {
// place the chosen tree, if the colours are right
index = -1;
Color col = mapColors[((w-1)-x)*w + y];
if (col.r>Threshold) {
index = 0;
} else if (col.g>Threshold) {
index = 1;
} else if (col.b>Threshold) {
index = 2;
}
if (index>=0) {
// place a tree
trees++;
TreeInstance instance = new TreeInstance();
float xpos = (float)x / (float)w; float ypos=(float)y / (float)w;
instance.position = new Vector3(xpos, 0.5f, ypos);
instance.color = Color.white;
instance.lightmapColor = Color.white;
instance.prototypeIndex = index;
instance.widthScale = 1.0f + Random.Range(-Variation,Variation);
instance.heightScale = 1.0f + Random.Range(-Variation,Variation);
instances.Add(instance);
}
}
}
Debug.Log("placed "+trees+" trees");
data.treeInstances = (TreeInstance[])instances.ToArray(typeof(TreeInstance));
// data.treeInstances = (TreeInstance[])instances.ToArray(typeof(TreeInstance));
data.RecalculateTreePositions();
}
}