2015-05-09 15:06:30 +00:00
|
|
|
|
using System;
|
2015-03-19 10:34:58 +00:00
|
|
|
|
using System.Collections;
|
2015-05-09 15:06:30 +00:00
|
|
|
|
using System.Linq;
|
2015-03-19 10:34:58 +00:00
|
|
|
|
using TransportGame.Generator;
|
|
|
|
|
using TransportGame.Model;
|
2015-06-02 17:50:59 +00:00
|
|
|
|
using TransportGame.Unity;
|
2015-05-09 15:06:30 +00:00
|
|
|
|
using TransportGame.Utils;
|
|
|
|
|
using UnityEngine;
|
2015-03-19 10:34:58 +00:00
|
|
|
|
|
|
|
|
|
public class TerrainGeneratorScript : MonoBehaviour
|
|
|
|
|
{
|
2015-05-08 09:44:25 +00:00
|
|
|
|
private Map map = null;
|
2015-03-19 10:34:58 +00:00
|
|
|
|
|
2015-05-08 09:44:25 +00:00
|
|
|
|
public int TerrainWidth = 1024;
|
|
|
|
|
public int TerrainHeight = 1024;
|
|
|
|
|
public GameObject WaterObject;
|
2015-05-09 15:06:30 +00:00
|
|
|
|
public Texture2D[] Textures;
|
2015-06-02 17:50:59 +00:00
|
|
|
|
public Material RoadMaterial;
|
2015-06-10 08:49:43 +00:00
|
|
|
|
public Material BuildingMaterial;
|
2015-03-19 10:34:58 +00:00
|
|
|
|
|
2015-05-08 09:44:25 +00:00
|
|
|
|
// Use this for initialization
|
|
|
|
|
void Start()
|
|
|
|
|
{
|
|
|
|
|
StartCoroutine(GenerateMap());
|
|
|
|
|
}
|
2015-03-19 10:34:58 +00:00
|
|
|
|
|
2015-05-08 09:44:25 +00:00
|
|
|
|
private void GenerateTerrainThread()
|
|
|
|
|
{
|
2015-05-22 08:26:29 +00:00
|
|
|
|
CityGenerator generator = new CityGenerator();
|
2015-05-08 09:44:25 +00:00
|
|
|
|
map = generator.Generate(TerrainWidth, TerrainHeight);
|
|
|
|
|
}
|
2015-05-20 08:26:46 +00:00
|
|
|
|
|
2015-05-08 09:44:25 +00:00
|
|
|
|
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[] {
|
2015-05-29 16:03:08 +00:00
|
|
|
|
new UnityEngine.Vector2(0, 0),
|
|
|
|
|
new UnityEngine.Vector2(0, 1),
|
|
|
|
|
new UnityEngine.Vector2(1, 0),
|
|
|
|
|
new UnityEngine.Vector2(1, 1)
|
2015-05-08 09:44:25 +00:00
|
|
|
|
};
|
|
|
|
|
water.RecalculateNormals();
|
2015-03-19 10:34:58 +00:00
|
|
|
|
|
2015-05-08 09:44:25 +00:00
|
|
|
|
return water;
|
|
|
|
|
}
|
2015-03-19 10:34:58 +00:00
|
|
|
|
|
2015-05-08 09:44:25 +00:00
|
|
|
|
private IEnumerator GenerateMap()
|
|
|
|
|
{
|
2015-05-20 08:26:46 +00:00
|
|
|
|
// Generate terrain
|
2015-06-10 08:49:43 +00:00
|
|
|
|
foreach (var i in Task.Await(GenerateTerrainThread))
|
2015-05-08 08:09:28 +00:00
|
|
|
|
yield return i;
|
2015-03-19 10:34:58 +00:00
|
|
|
|
|
2015-05-08 09:44:25 +00:00
|
|
|
|
// 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);
|
2015-05-20 08:26:46 +00:00
|
|
|
|
terrainData.SetHeights(0, 0, map.Heightmap);
|
2015-05-08 09:44:25 +00:00
|
|
|
|
terrainData.name = "Generated Terrain Data";
|
2015-05-09 15:06:30 +00:00
|
|
|
|
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 = Textures.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;
|
|
|
|
|
}
|
2015-03-19 10:34:58 +00:00
|
|
|
|
|
2015-05-08 09:44:25 +00:00
|
|
|
|
// Create terrain object
|
|
|
|
|
GameObject terrain = Terrain.CreateTerrainGameObject(terrainData);
|
|
|
|
|
terrain.name = "Generated Terrain";
|
|
|
|
|
yield return null;
|
2015-03-19 10:34:58 +00:00
|
|
|
|
|
2015-05-08 09:44:25 +00:00
|
|
|
|
Terrain terrainComp = terrain.GetComponent<Terrain>();
|
|
|
|
|
terrainComp.heightmapPixelError = 1;
|
|
|
|
|
yield return null;
|
2015-03-19 10:34:58 +00:00
|
|
|
|
|
2015-05-08 09:44:25 +00:00
|
|
|
|
// Set water
|
|
|
|
|
if (WaterObject != null)
|
|
|
|
|
{
|
|
|
|
|
MeshFilter waterMesh = WaterObject.GetComponent<MeshFilter>();
|
|
|
|
|
waterMesh.mesh = GenerateWater();
|
|
|
|
|
}
|
2015-05-09 15:06:30 +00:00
|
|
|
|
yield return null;
|
2015-05-08 08:09:28 +00:00
|
|
|
|
|
2015-06-02 17:50:59 +00:00
|
|
|
|
// Set up textures
|
|
|
|
|
Logger.Info("Setting up textures...");
|
2015-06-10 08:49:43 +00:00
|
|
|
|
BeginSetupSplatmaps(terrainData);
|
2015-06-02 17:50:59 +00:00
|
|
|
|
|
2015-06-10 08:49:43 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-10 11:43:57 +00:00
|
|
|
|
// Generate road & building mesh (we run the loops in parallel), and apply textures
|
2015-06-10 08:49:43 +00:00
|
|
|
|
Logger.Info("Generating buildings and roads...");
|
2015-05-29 16:03:08 +00:00
|
|
|
|
|
2015-06-10 08:49:43 +00:00
|
|
|
|
BuildingMeshGenerator buildingMeshGenerator = new BuildingMeshGenerator();
|
|
|
|
|
buildingMeshGenerator.BuildingMaterial = BuildingMaterial;
|
2015-05-29 16:03:08 +00:00
|
|
|
|
|
2015-06-10 08:49:43 +00:00
|
|
|
|
RoadMeshGenerator roadMeshGenerator = new RoadMeshGenerator();
|
|
|
|
|
roadMeshGenerator.RoadMaterial = RoadMaterial;
|
|
|
|
|
|
2015-06-10 11:43:57 +00:00
|
|
|
|
foreach (var i in Task.InParallel(buildingMeshGenerator.Generate(map), roadMeshGenerator.Generate(map), EndSetupSplatmaps(terrainData)))
|
2015-06-10 08:49:43 +00:00
|
|
|
|
yield return i;
|
2015-05-08 09:44:25 +00:00
|
|
|
|
}
|
2015-03-19 10:34:58 +00:00
|
|
|
|
|
2015-06-02 17:50:59 +00:00
|
|
|
|
private float[,,] GenerateSplatData(int alphaWidth, int alphaHeight, int alphaLayers)
|
2015-05-09 15:06:30 +00:00
|
|
|
|
{
|
2015-06-02 17:50:59 +00:00
|
|
|
|
float[, ,] splatData = new float[alphaWidth, alphaHeight, alphaLayers];
|
|
|
|
|
Expression[] expressions = new Expression[alphaLayers];
|
2015-05-09 15:06:30 +00:00
|
|
|
|
|
2015-06-02 17:50:59 +00:00
|
|
|
|
for (int y = 0; y < alphaHeight; y++)
|
|
|
|
|
for (int x = 0; x < alphaWidth; x++)
|
2015-05-09 15:06:30 +00:00
|
|
|
|
{
|
2015-06-02 17:50:59 +00:00
|
|
|
|
float y_01 = (float)y / (float)alphaHeight;
|
|
|
|
|
float x_01 = (float)x / (float)alphaWidth;
|
2015-05-09 15:06:30 +00:00
|
|
|
|
|
|
|
|
|
int ix = Mathf.RoundToInt(x_01 * TerrainWidth);
|
|
|
|
|
int iy = Mathf.RoundToInt(y_01 * TerrainHeight);
|
|
|
|
|
|
|
|
|
|
// Get height
|
2015-05-20 08:26:46 +00:00
|
|
|
|
float height = map.GetHeight(ix, iy);
|
2015-06-02 17:50:59 +00:00
|
|
|
|
|
2015-05-09 15:06:30 +00:00
|
|
|
|
// Get steepness
|
2015-05-21 08:49:04 +00:00
|
|
|
|
float steepness = map.GetSteepness(ix, iy);
|
2015-06-02 17:50:59 +00:00
|
|
|
|
|
2015-05-20 08:26:46 +00:00
|
|
|
|
// Go through each texture layer
|
2015-06-02 17:50:59 +00:00
|
|
|
|
float[] weights = new float[alphaLayers];
|
2015-05-09 15:06:30 +00:00
|
|
|
|
float sum = 0;
|
|
|
|
|
|
2015-06-02 17:50:59 +00:00
|
|
|
|
for (int t = 0; t < alphaLayers; t++)
|
2015-05-09 15:06:30 +00:00
|
|
|
|
{
|
|
|
|
|
// 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;
|
2015-06-02 17:50:59 +00:00
|
|
|
|
expressions[t].Variables["maxHeight"] = map.Biome.Height;
|
2015-05-09 15:06:30 +00:00
|
|
|
|
expressions[t].Variables["waterLevel"] = map.WaterLevel - 1f;
|
|
|
|
|
|
|
|
|
|
// Obtain weight
|
|
|
|
|
weights[t] = expressions[t].Evaluate();
|
|
|
|
|
sum += weights[t];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Normalize and copy weights
|
2015-06-02 17:50:59 +00:00
|
|
|
|
for (int t = 0; t < alphaLayers; t++)
|
2015-05-09 15:06:30 +00:00
|
|
|
|
{
|
|
|
|
|
splatData[x, y, t] = weights[t] / sum;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-02 17:50:59 +00:00
|
|
|
|
return splatData;
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-10 08:49:43 +00:00
|
|
|
|
float[, ,] splatData = null;
|
|
|
|
|
private Task splatmapsTask = null;
|
|
|
|
|
|
|
|
|
|
private void BeginSetupSplatmaps(TerrainData terrainData)
|
2015-06-02 17:50:59 +00:00
|
|
|
|
{
|
|
|
|
|
int alphaW = terrainData.alphamapWidth;
|
|
|
|
|
int alphaH = terrainData.alphamapHeight;
|
|
|
|
|
int alphaL = terrainData.alphamapLayers;
|
|
|
|
|
|
2015-06-10 08:49:43 +00:00
|
|
|
|
splatmapsTask = Task.RunAsync(() => splatData = GenerateSplatData(alphaW, alphaH, alphaL));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private IEnumerable EndSetupSplatmaps(TerrainData terrainData)
|
|
|
|
|
{
|
|
|
|
|
foreach (var i in splatmapsTask.Await())
|
2015-06-02 17:50:59 +00:00
|
|
|
|
yield return i;
|
|
|
|
|
|
2015-05-09 15:06:30 +00:00
|
|
|
|
terrainData.SetAlphamaps(0, 0, splatData);
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-08 09:44:25 +00:00
|
|
|
|
// Update is called once per frame
|
|
|
|
|
void Update()
|
|
|
|
|
{
|
|
|
|
|
}
|
2015-05-29 16:03:08 +00:00
|
|
|
|
|
|
|
|
|
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.");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-03-19 10:34:58 +00:00
|
|
|
|
}
|