Skip to content

Instantly share code, notes, and snippets.

@m-biernat
Created February 24, 2024 16:02
Show Gist options
  • Save m-biernat/9c357afe55fcd662be6491e64ba7ded1 to your computer and use it in GitHub Desktop.
Save m-biernat/9c357afe55fcd662be6491e64ba7ded1 to your computer and use it in GitHub Desktop.
A simple Unity Editor tool that converts trees in a Terrain into GameObjects
// This is a simple Unity Editor tool that converts trees in a Terrain (with multi-terrain support) into GameObjects.
// It should be placed in the Assets/Editor directory. Do not use it without any VCS!
// Copyright (c) 2023 Michał Biernat @m-biernat, licensed under the MIT License.
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
public class TreeToGameObjectConverter : EditorWindow
{
readonly string _rootName = "CONVERTED TREES";
GameObject _root;
List<Terrain> _terrainList;
[MenuItem("Tools/Tree to GameObject Converter")]
public static void ShowEditorWindow()
{
EditorWindow window = GetWindow<TreeToGameObjectConverter>();
window.titleContent = new GUIContent("Tree to GameObject Converter");
}
void OnGUI()
{
if (GUILayout.Button("Add Selected Terrain Objects", GUILayout.Height(40)))
AddSelectedTerrainObjects();
GUILayout.Space(10);
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
if (_terrainList == null || _terrainList.Count == 0)
GUILayout.Label("No Terrain Objects Selected");
else
GUILayout.Label($"Currently Selected {_terrainList.Count} Terrain Objects");
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.Space(10);
if (GUILayout.Button($"Convert Trees into GameObjects", GUILayout.Height(40)))
ConvertTreesToGameObjects();
if (GUILayout.Button($"Remove \"{_rootName}\"", GUILayout.Height(40)))
RemoveConverted();
GUILayout.Space(10);
GUI.color = Color.red;
if (GUILayout.Button($"Remove Trees from Terrain Objects", GUILayout.Height(40)))
RemoveTrees();
}
void AddSelectedTerrainObjects()
{
_terrainList = new List<Terrain>();
foreach (var selectedGO in Selection.gameObjects)
{
Terrain terrain;
if (selectedGO.TryGetComponent(out terrain))
_terrainList.Add(terrain);
}
}
void ConvertTreesToGameObjects()
{
if (GetRoot() == null)
_root = new GameObject(_rootName);
else
{
Debug.LogError($"Object with the name \"{_rootName}\" already exists");
return;
}
int numOfTrees = 0;
foreach (var terrain in _terrainList)
{
numOfTrees += ConvertTrees(terrain);
}
Debug.Log($"Converted {numOfTrees} Trees into GameObjects");
}
GameObject GetRoot()
=> GameObject.Find(_rootName);
int ConvertTrees(Terrain terrain)
{
var treeInstances = terrain.terrainData.treeInstances;
var treePrototypes = terrain.terrainData.treePrototypes;
if (treeInstances.Length == 0 && treePrototypes.Length == 0)
return 0;
var parent = new GameObject(terrain.name);
parent.transform.position = terrain.transform.position;
parent.transform.SetParent(_root.transform);
foreach (var treeInstance in treeInstances)
{
var treePrototype = treePrototypes[treeInstance.prototypeIndex];
var go = Instantiate(treePrototype.prefab, parent.transform);
go.transform.localPosition = Vector3.Scale(treeInstance.position,
terrain.terrainData.size);
go.transform.localScale = new Vector3(treeInstance.widthScale,
treeInstance.widthScale,
treeInstance.heightScale);
go.transform.Rotate(0, treeInstance.rotation * Mathf.Rad2Deg, 0);
}
return treeInstances.Length;
}
void RemoveTrees()
{
foreach (var terrain in _terrainList)
{
terrain.terrainData.treeInstances = new TreeInstance[0];
terrain.terrainData.treePrototypes = new TreePrototype[0];
}
}
void RemoveConverted()
=> DestroyImmediate(GetRoot());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment