using System.Collections.Generic; using UnityEngine; using UnityEngine.Assertions; public class GrassField : MonoBehaviour { public int size = 100; public int numChunks = 5; public GrassChunk chunkPrefab; private struct BladeData { public Vector3 rootPosition; public Vector3 tipPosition; } public void GenerateGrassField() { Debug.Log("Generating grass field... "); var blades = GenerateBlades(); CreateChunks(blades); Debug.Log("Generating grass field done"); } private BladeData[] GenerateBlades() { var blades = new BladeData[size * size]; for (int x = 0; x < size; x++) for (int y = 0; y < size; y++) { var i = x + y * size; blades[i].rootPosition = new Vector3(x + 0.5f, 0.0f, y + 0.5f); blades[i].tipPosition = blades[i].rootPosition + new Vector3(0.0f, 1.0f, 0.0f); } return blades; } private void CreateChunks(BladeData[] blades) { GameObject chunks; // If a child called "Chunks" exists, destroy it -> children are also destroyed var chunksTransform = gameObject.transform.Find("Chunks"); if (chunksTransform != null) { // Destroy(obj) does not work in edit mode DestroyImmediate(chunksTransform.gameObject); } // create GameObject "Chunks" as a container for all GrassChunk objects chunks = new GameObject("Chunks"); chunks.transform.SetParent(this.transform, false); var chunkSize = ((float)size) / numChunks; for (int x = 0; x < numChunks; x++) for (int y = 0; y < numChunks; y++) { var chunkPos = new Vector3(x, 0.0f, y) * chunkSize; // bounds in local space of grass field var bounds = new Bounds( new Vector3(0.5f * chunkSize, 0.5f, 0.5f * chunkSize), new Vector3(chunkSize, 1.01f, chunkSize) ); List rootPositions = new(); List tipPositions = new(); List indices = new(); foreach (var blade in blades) { var localRootPos = blade.rootPosition - chunkPos; if (bounds.Contains(localRootPos)) { rootPositions.Add(localRootPos); var localTipPos = blade.tipPosition - chunkPos; tipPositions.Add(localTipPos); indices.Add(rootPositions.Count - 1); } } var mesh = new Mesh(); // 16 bit (default) supports 65536 vertices, 32 bit supports 4 billion // mesh.indexFormat = UnityEngine.Rendering.IndexFormat.UInt32; mesh.name = "grass_chunk[" + x + "," + y + "]"; mesh.SetVertices(rootPositions); // do not calculate bounding box - will be set manually mesh.SetIndices(indices, MeshTopology.Points, 0, false); // Vector3 UVs are possible mesh.SetUVs(0, tipPositions); var chunk = Instantiate(chunkPrefab, chunks.transform); chunk.name = "Chunk [" + x + ", " + y +"]"; chunk.transform.localPosition = chunkPos; var meshFilter = chunk.GetComponent(); meshFilter.sharedMesh = mesh; var meshRenderer = chunk.GetComponent(); meshRenderer.localBounds = bounds; } } }