95 lines
3.4 KiB
C#
95 lines
3.4 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class GrassField : MonoBehaviour {
|
|
public int size = 100;
|
|
public int numChunks = 5;
|
|
public Material grassMaterial;
|
|
public GrassChunk chunkPrefab;
|
|
|
|
private struct BladeData {
|
|
public Vector3 rootPosition;
|
|
public Vector3 tipOffset;
|
|
}
|
|
|
|
// ReSharper disable Unity.PerformanceAnalysis
|
|
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 * 100];
|
|
|
|
for (int x = 0; x < size * 10; x++)
|
|
for (int y = 0; y < size * 10; y++) {
|
|
var i = x + y * size * 10;
|
|
blades[i].rootPosition = new Vector3(x/10.0f, 0.0f, y/10.0f);
|
|
blades[i].tipOffset = new Vector3(0.01f, 1.0f, 0.01f);
|
|
}
|
|
|
|
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<Vector3> rootPositions = new();
|
|
List<Vector3> tipOffsets = new();
|
|
List<int> indices = new();
|
|
foreach (var blade in blades) {
|
|
var localRootPos = blade.rootPosition - chunkPos;
|
|
if (bounds.Contains(localRootPos)) {
|
|
rootPositions.Add(localRootPos);
|
|
tipOffsets.Add(blade.tipOffset);
|
|
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, tipOffsets);
|
|
|
|
var chunk = Instantiate(chunkPrefab, chunks.transform);
|
|
chunk.name = "Chunk [" + x + ", " + y +"]";
|
|
chunk.transform.localPosition = chunkPos;
|
|
|
|
var meshFilter = chunk.GetComponent<MeshFilter>();
|
|
meshFilter.sharedMesh = mesh;
|
|
|
|
var meshRenderer = chunk.GetComponent<MeshRenderer>();
|
|
meshRenderer.localBounds = bounds;
|
|
meshRenderer.sharedMaterial = grassMaterial;
|
|
}
|
|
}
|
|
}
|