using UnityEngine; using System.Threading; using System; using System.Collections.Generic; using JetBrains.Annotations; public class MapGenerator : MonoBehaviour { public enum DrawMode {NoiseMap, ColorMap, Mesh, FalloffMap}; public DrawMode drawMode; public TerrainData terrainData; public NoiseData noiseData; [Range(0,6)] public int levelOfDetail; public bool autoUpdate; public TerrainType[] regions; static MapGenerator instance; float[,] falloffMap; Queue> mapDataThreadInfoQueue = new Queue>(); Queue> meshDataThreadInfoQueue = new Queue>(); private void Awake() { falloffMap = FalloffGenerator.GenerateFalloffMap(mapChunkSize); } void OnValuesUpdated() { if (!Application.isPlaying) { DrawMapInEditor(); } } public static int mapChunkSize { get { if(instance == null) { instance = FindFirstObjectByType(); } if (instance.terrainData.useFlatShading) { return 95; } else { return 239; } } } public void DrawMapInEditor() { MapData mapData = GenerateMapData(Vector2.zero); MapDisplay display = FindFirstObjectByType(); if (drawMode == DrawMode.NoiseMap) { display.DrawTexture(TextureGenerator.TextureFromHeightMap(mapData.heightMap)); } else if (drawMode == DrawMode.ColorMap) { display.DrawTexture(TextureGenerator.TextureFromColorMap(mapData.colorMap, mapChunkSize, mapChunkSize)); } else if (drawMode == DrawMode.Mesh) { display.DrawMesh(MeshGenerator.GenerateTerrainMesh(mapData.heightMap, terrainData.meshHeightMultiplier, terrainData.meshHeightCurve, levelOfDetail, terrainData.useFlatShading), TextureGenerator.TextureFromColorMap(mapData.colorMap, mapChunkSize, mapChunkSize)); } else if(drawMode == DrawMode.FalloffMap) { display.DrawTexture(TextureGenerator.TextureFromHeightMap(FalloffGenerator.GenerateFalloffMap(mapChunkSize))); } } public void RequestMapData(Vector2 centre, Action callback) { ThreadStart threadStart = delegate { MapDataThread(centre, callback); }; new Thread(threadStart).Start(); } void MapDataThread(Vector2 centre, Action callback) { MapData mapData = GenerateMapData(centre); lock (mapDataThreadInfoQueue) { mapDataThreadInfoQueue.Enqueue(new MapThreadInfo(callback, mapData)); } } public void RequestMeshData(MapData mapData, int lod, Action callback) { ThreadStart threadStart = delegate { MeshDataThread(mapData, lod, callback); }; new Thread(threadStart).Start(); } void MeshDataThread(MapData mapData, int lod, Action callback) { MeshData meshData = MeshGenerator.GenerateTerrainMesh(mapData.heightMap, terrainData.meshHeightMultiplier, terrainData.meshHeightCurve, lod, terrainData.useFlatShading); lock (meshDataThreadInfoQueue) { meshDataThreadInfoQueue.Enqueue(new MapThreadInfo(callback, meshData)); } } private void Update() { if(mapDataThreadInfoQueue.Count > 0) { for(int i = 0; i < mapDataThreadInfoQueue.Count; i++) { MapThreadInfo threadInfo = mapDataThreadInfoQueue.Dequeue(); threadInfo.callback(threadInfo.parameter); } } if (meshDataThreadInfoQueue.Count > 0) { for(int i = 0; i < meshDataThreadInfoQueue.Count; i++) { MapThreadInfo threadInfo = meshDataThreadInfoQueue.Dequeue(); threadInfo.callback(threadInfo.parameter); } } } MapData GenerateMapData(Vector2 centre) { float[,] noiseMap = Noise.GenerateNoiseMap(mapChunkSize + 2, mapChunkSize + 2, noiseData.seed, noiseData.noiseScale, noiseData.octaves, noiseData.persistance, noiseData.lacunarity, centre + noiseData.offset, noiseData.normalizeMode); Color[] colorMap = new Color[mapChunkSize * mapChunkSize]; for (int y = 0; y < mapChunkSize; y++) { for (int x = 0; x < mapChunkSize; x++) { if (terrainData.useFalloff) { noiseMap[x, y] = Mathf.Clamp01(noiseMap[x, y] - falloffMap[x, y]); } float currentHeight = noiseMap[x, y]; for(int i = 0; i < regions.Length; i++) { if(currentHeight >= regions[i].height) { colorMap[y * mapChunkSize + x] = regions[i].color; } else { break; } } } } return new MapData(noiseMap, colorMap); } private void OnValidate() { if(terrainData != null) { terrainData.OnValuesUpdated -= OnValuesUpdated; terrainData.OnValuesUpdated += OnValuesUpdated; } if(noiseData != null) { noiseData.OnValuesUpdated -= OnValuesUpdated; noiseData.OnValuesUpdated += OnValuesUpdated; } falloffMap = FalloffGenerator.GenerateFalloffMap(mapChunkSize); } struct MapThreadInfo { public readonly Action callback; public readonly T parameter; public MapThreadInfo(Action callback, T parameter) { this.callback = callback; this.parameter = parameter; } } } [System.Serializable] public struct TerrainType { public string name; public float height; public Color color; } public struct MapData { public readonly float[,] heightMap; public readonly Color[] colorMap; public MapData(float[,] heightMap, Color[] colorMap) { this.heightMap = heightMap; this.colorMap = colorMap; } }