Procedural generating - generating trees at given mesh - vertices/height
Hi! I'm currently working on procedural generation of a terrain! I've managed to find tutorial about it but I got to the moment where I had to spawn the trees. My idea was getting a random vertices from the generated mesh and spawning the trees on them.After many hours of struggling and looking around the internet I've finally found a solution for this problem too. But the trees are not spawning where I want them! My mesh is split on couple of regions and i want to spawn them on a given region. This is what i mean. So my question is how can I spawn them on the grass region? Before I post the script: I'm using empty objects as spawn points and generating them on random vertices of the generated mesh. Then I'm instantiating the trees on those points.
And these are the tutorials I used: Credit ---
https://www.youtube.com/watch?v=wbpMiKiSKm8&list=PLFt_AvWsXl0eBW2EiBtl_sxmDtSgZBxB3∈dex=1
https://gist.github.com/v21/5378391
Here is my script:
public class MapGenerator : MonoBehaviour { public enum DrawMode { NoiseMap, ColorMap, Mesh, FallofMap } public DrawMode drawMode; const int mapChunkSize = 241; public float noiseScale; [Range(0, 6)] public int levelOfDetail; public int octaves; [Range(0, 1)] public float persistance; public float lacunarity; public int seed; public Vector2 offset; public bool useFalloff; public GameObject mesh; public MeshCollider mCollider; #region TreeSpawnPoints public List<Transform> tree1SpawnPoints = new List<Transform>(); public List<Transform> tree2SpawnPoints = new List<Transform>(); public List<Transform> tree3SpawnPoints = new List<Transform>(); public List<GameObject> treeObj = new List<GameObject>(); #endregion public float meshHeightMultiplier; public AnimationCurve meshHeightCurve; public bool autoUpdate; [Range(0, 5)] public float timerBeforeTreeSpawn = 10; public float timer = 0; bool timerEnd = false; private Vector3 randomPoint; public TerrainType[] regions; public bool isSpawned = true; float[,] fallofMap; //Generating map on awake with random seed and generating collider after the map is generated private void Awake() { seed = Random.Range(0, 800); GenerateMap(); fallofMap = FallOfGenerator.GenerateFallofMap(mapChunkSize); if (mesh.GetComponent<MeshCollider>() == true) { Destroy(mesh.GetComponent<MeshCollider>()); mesh.AddComponent<MeshCollider>(); } } private void Start() { mCollider = mesh.GetComponent<MeshCollider>(); } private void Update() { //timer for late tree spawn so we can generate mesh and spawn them on the new mesh if(!timerEnd) { timer += Time.deltaTime; if (timer > timerBeforeTreeSpawn) { mCollider = mesh.GetComponent<MeshCollider>(); GenerateMapTrees(); timerEnd = true; } } } //generating the noise map on object public void GenerateMapTrees() { for (int y = 0; y < mapChunkSize; y++) { for (int x = 0; x < mapChunkSize; x++) { //checking if isSpawned is true so we can spawn trees only 1 time if (isSpawned) { //TREE AT LAND if (regions[2].height > regions[1].height) { //generatin trees at the given empty objects that are spawned randomly on the generated mesh //and we're getting on of the vertices on the mesh and than we're spawning the empty object so //we can spawn the given tree object //looping through the whole list so we can generate tree for each spawn point //generating random spawnpoints on the collider of the object //TREE 1 AT LAND for (int t1 = 0; t1 < tree1SpawnPoints.Count; t1++) { for (int _t1 = 0; _t1 < regions[2]._tree1SpawnPoints.Count; _t1++) { Vector3 randomPoint = GetRandomPointOnMesh(mCollider.sharedMesh); randomPoint += mCollider.transform.position; float coords1X = randomPoint.x; float coords1Y = randomPoint.y; float coords1Z = randomPoint.z; Instantiate(regions[2]._tree1SpawnPoints[_t1], new Vector3(coords1X, coords1Y, coords1Z), regions[2]._tree1SpawnPoints[_t1].transform.rotation); regions[2]._treeObj = treeObj[2]; tree1SpawnPoints[y * mapChunkSize + x] = regions[2]._tree1SpawnPoints[t1]; Instantiate(regions[2]._treeObj, new Vector3(coords1X, coords1Y, coords1Z), regions[2]._tree1SpawnPoints[t1].transform.rotation); } } } } isSpawned = false; } } } public void GenerateMap() { //saving the generated noise map from the noise GenScript float[,] noiseMap = NoiseGen.GenerateNoiseMap(mapChunkSize, mapChunkSize, seed, noiseScale, octaves, persistance, lacunarity, offset); Color[] colorMap = new Color[mapChunkSize * mapChunkSize]; for (int y = 0; y < mapChunkSize; y++) { for (int x = 0; x < mapChunkSize; x++) { //clamping the noise map to be between 0 and 1 from the generated falloff map if (useFalloff) { noiseMap[x, y] = Mathf.Clamp01(noiseMap[x, y] - fallofMap[x, y]); } float currentHeight = noiseMap[x, y]; //setting the collors of each region for (int i = 0; i < regions.Length; i++) { if (currentHeight <= regions[i].height) { colorMap[y * mapChunkSize + x] = regions[i].color; break; } } } } #region IfNeeded //displaying solo maps if choosen MapDisplay display = FindObjectOfType<MapDisplay>(); if (drawMode == DrawMode.NoiseMap) { display.DrawTexture(TextureGenerator.TextureFromHeightMap(noiseMap)); } else if (drawMode == DrawMode.ColorMap) { display.DrawTexture(TextureGenerator.TextureFromColorMap(colorMap, mapChunkSize, mapChunkSize)); } else if (drawMode == DrawMode.Mesh) { display.DrawMesh(MeshGenerator.GenerateTerrainMesh(noiseMap, meshHeightMultiplier, meshHeightCurve, levelOfDetail), TextureGenerator.TextureFromColorMap(colorMap, mapChunkSize, mapChunkSize)); } else if (drawMode == DrawMode.FallofMap) { display.DrawTexture(TextureGenerator.TextureFromHeightMap(FallOfGenerator.GenerateFallofMap(mapChunkSize))); } #endregion } //with this method a random point on the mesh is generated Vector3 GetRandomPointOnMesh(Mesh mesh) { float[] sizes = GetTriSizes(mesh.triangles, mesh.vertices); float[] cSizes = new float[sizes.Length]; float total = 0; for (int i = 0; i < sizes.Length; i++) { total += sizes[i]; cSizes[i] = total; } float randomSample = Random.value * total; int triIndex = -1; for (int l = 0; l < sizes.Length; l++) { if (randomSample <= cSizes[l]) { triIndex = l; break; } } if (triIndex == -1) { Debug.LogError("triIndex shold never be -1"); } Vector3 a = mesh.vertices[mesh.triangles[triIndex * 3]]; Vector3 b = mesh.vertices[mesh.triangles[triIndex * 3 + 1]]; Vector3 c = mesh.vertices[mesh.triangles[triIndex * 3 + 2]]; float r = Random.value; float s = Random.value; if (r + s >= 1) { r = 1 - r; s = 1 - s; } Vector3 pointOnMesh = a + r * (b - a) + s * (c - a); return pointOnMesh; } float[] GetTriSizes(int[] tris, Vector3[] verts) { int triCont = tris.Length / 3; float[] sizes = new float[triCont]; for (int i = 0; i < triCont; i++) { sizes[i] = .5f * Vector3.Cross(verts[tris[i * 3 + 1]] - verts[tris[i * 3]], verts[tris[i * 3 + 2]] - verts[tris[i * 3]]).magnitude; } return sizes; } //clamping values private void OnValidate() { if (lacunarity < 1) { lacunarity = 1; } if (octaves < 0) { octaves = 0; } fallofMap = FallOfGenerator.GenerateFallofMap(mapChunkSize); } } [System.Serializable] public struct TerrainType { public string name; public float height; public Color color; public List<Transform> _tree1SpawnPoints; public List<Transform> _tree2SpawnPoints; public List<Transform> _tree3SpawnPoints; public GameObject _treeObj; }