hacking tree placement

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.

what type does treeInstances expect? because you assign object[ ] to it.

Hm, yes. Could be that the casting is off. I’ve had to mix a C# and a JS script to get this.

I think it expects an array of TreeInstances. But I’m not sure.

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”

Seems to me you want ToBuiltin, not ToArray.

–Eric

Apparently not:
Assets/Editor/SplatTrees.js(66,43): BCE0019: ‘ToBuiltin’ is not a member of ‘System.Collections.ArrayList’.

Ok, the C# script I worked of did place trees. So I’ll try the reverse route and convert the JS parts to C# instead.

Might be that it is my tree placement (the calculation of the position Vector3) which is in error.

That’s what I learnt from building up the C# version, which worked for a moment, then also beachballs out.

Will keep you posted.

Got it!

Welcome to automated tree generation:

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

Some more tweaking and … welcome to the forest. :slight_smile:

Oops, right…ToBuiltin is for Javascript arrays and you’re using ArrayList, sorry.

–Eric

Ok does this also addresses the need to have differently rotated trees ? Or are these billion trees all facing the same direction? :slight_smile:

I think this is one of the most commonly felt problem by the people approacching unity for the very first time.

Ok… let’s hope there will be a way to fix this… sooner or later… :slight_smile:
Bye! :slight_smile: