using System; using System.Collections; using System.Linq; using TransportGame.Generator; using TransportGame.Model; using TransportGame.Unity; using TransportGame.Utils; using UnityEngine; public class TerrainGeneratorScript : MonoBehaviour { private Map map = null; public int TerrainWidth = 1024; public int TerrainHeight = 1024; public GameObject WaterObject; public Texture2D[] TerrainTextures; public Texture2D[] BuildingTextures; public Material RoadMaterial; public Material BuildingMaterial; // Use this for initialization void Start() { StartCoroutine(GenerateMap()); } private void GenerateTerrainThread() { CityGenerator generator = new CityGenerator(); map = generator.Generate(TerrainWidth, TerrainHeight); } private Mesh GenerateWater() { Mesh water = new Mesh(); water.name = "water"; water.vertices = new[] { new Vector3(0, map.WaterLevel, 0), new Vector3(0, map.WaterLevel, map.Height), new Vector3(map.Width, map.WaterLevel, 0), new Vector3(map.Width, map.WaterLevel, map.Height) }; water.triangles = new[] { 0, 1, 2, 2, 1, 3 }; water.uv = new[] { new UnityEngine.Vector2(0, 0), new UnityEngine.Vector2(0, 1), new UnityEngine.Vector2(1, 0), new UnityEngine.Vector2(1, 1) }; water.RecalculateNormals(); return water; } private IEnumerator GenerateMap() { // Generate terrain foreach (var i in Task.Await(GenerateTerrainThread)) yield return i; // Generate terrain data TerrainData terrainData = new TerrainData(); terrainData.heightmapResolution = Mathf.Max(map.Height, map.Width) + 1; terrainData.size = new Vector3(map.Width, map.Biome.Height, map.Height); terrainData.SetDetailResolution(1024, 8); terrainData.SetHeights(0, 0, map.Heightmap); terrainData.name = "Generated Terrain Data"; yield return null; if (map.Biome.Textures != null) { SplatPrototype[] prototypes = new SplatPrototype[map.Biome.Textures.Length]; for (int i = 0; i < map.Biome.Textures.Length; i++) { Texture2D texture = TerrainTextures.FirstOrDefault(tex => tex != null && tex.name == map.Biome.Textures[i].Source); if (texture == null) throw new Exception("Texture " + map.Biome.Textures[i].Source + " not found!"); prototypes[i] = new SplatPrototype(); prototypes[i].texture = texture; } terrainData.splatPrototypes = prototypes; yield return null; } // Create terrain object GameObject terrain = Terrain.CreateTerrainGameObject(terrainData); terrain.name = "Generated Terrain"; yield return null; Terrain terrainComp = terrain.GetComponent(); terrainComp.heightmapPixelError = 1; yield return null; // Set water if (WaterObject != null) { MeshFilter waterMesh = WaterObject.GetComponent(); waterMesh.mesh = GenerateWater(); } yield return null; // Set up textures Logger.Info("Setting up textures..."); BeginSetupSplatmaps(terrainData); foreach (var lot in map.BuildingLots) { for (int i = 0; i < lot.Points.Length; i++) { int j = (i + 1) % lot.Points.Length; if (!map.IsInside(lot.Points[i])) { Logger.Warning("Generated point not inside: {0}", lot.Points[i]); continue; } if (!map.IsInside(lot.Points[j])) { Logger.Warning("Generated point not inside: {0}", lot.Points[j]); continue; } Debug.DrawLine( new Vector3(lot.Points[i].Y, map.GetHeight((int)lot.Points[i].X, (int)lot.Points[i].Y) + 0.5f, lot.Points[i].X), new Vector3(lot.Points[j].Y, map.GetHeight((int)lot.Points[j].X, (int)lot.Points[j].Y) + 0.5f, lot.Points[j].X), Color.yellow, 10000); } } // Generate road & building mesh (we run the loops in parallel), and apply textures Logger.Info("Generating buildings and roads..."); BuildingMeshGenerator buildingMeshGenerator = new BuildingMeshGenerator(); buildingMeshGenerator.BuildingMaterial = BuildingMaterial; buildingMeshGenerator.Textures = BuildingTextures; RoadMeshGenerator roadMeshGenerator = new RoadMeshGenerator(); roadMeshGenerator.RoadMaterial = RoadMaterial; foreach (var i in Task.InParallel(buildingMeshGenerator.Generate(map), roadMeshGenerator.Generate(map), EndSetupSplatmaps(terrainData))) yield return i; } private float[,,] GenerateSplatData(int alphaWidth, int alphaHeight, int alphaLayers) { float[, ,] splatData = new float[alphaWidth, alphaHeight, alphaLayers]; Expression[] expressions = new Expression[alphaLayers]; for (int y = 0; y < alphaHeight; y++) for (int x = 0; x < alphaWidth; x++) { float y_01 = (float)y / (float)alphaHeight; float x_01 = (float)x / (float)alphaWidth; int ix = Mathf.RoundToInt(x_01 * TerrainWidth); int iy = Mathf.RoundToInt(y_01 * TerrainHeight); // Get height float height = map.GetHeight(ix, iy); // Get steepness float steepness = map.GetSteepness(ix, iy); // Go through each texture layer float[] weights = new float[alphaLayers]; float sum = 0; for (int t = 0; t < alphaLayers; t++) { // Set up expression if (expressions[t] == null) { expressions[t] = new Expression(map.Biome.Textures[t].Expression); expressions[t].ParseExpression(); } expressions[t].Variables["height"] = height; expressions[t].Variables["steepness"] = steepness; expressions[t].Variables["maxHeight"] = map.Biome.Height; expressions[t].Variables["waterLevel"] = map.WaterLevel - 1f; // Obtain weight weights[t] = expressions[t].Evaluate(); sum += weights[t]; } // Normalize and copy weights for (int t = 0; t < alphaLayers; t++) { splatData[x, y, t] = weights[t] / sum; } } return splatData; } float[, ,] splatData = null; private Task splatmapsTask = null; private void BeginSetupSplatmaps(TerrainData terrainData) { int alphaW = terrainData.alphamapWidth; int alphaH = terrainData.alphamapHeight; int alphaL = terrainData.alphamapLayers; splatmapsTask = Task.RunAsync(() => splatData = GenerateSplatData(alphaW, alphaH, alphaL)); } private IEnumerable EndSetupSplatmaps(TerrainData terrainData) { foreach (var i in splatmapsTask.Await()) yield return i; terrainData.SetAlphamaps(0, 0, splatData); } // Update is called once per frame void Update() { } void OnGUI() { Event e = Event.current; if (e.type == EventType.KeyDown) { if (e.keyCode == KeyCode.Home) { Logger.Warning("Writing to file..."); Logger.DumpMap(map, "map.map"); Logger.Warning("Wrote map to file."); } } } }