inz-00/Assets/Scripts/LandmassGenerator.cs
2019-11-17 20:33:32 +01:00

305 lines
11 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Assets.AnnotationPass;
using Assets.Cities;
using Assets.Common;
using Assets.GridGenerator;
using Assets.PointDistribution;
using Assets.Utils;
using UnityEngine;
using UnityEngine.Serialization;
using Random = System.Random;
using Tree = Assets.Common.Tree;
namespace Assets
{
[Serializable]
public class GraphGeneratorDebug
{
public bool enabled = true;
public bool displayPoints = true;
public bool displayBoundary = true;
public bool displayVertices = true;
public bool displayNeighbours = false;
public bool displayLabels = false;
public bool displayEdges = true;
public bool displayLocations = true;
public bool displayLocationCells = true;
public bool displayLocationEdges = true;
public bool displayCities = true;
public bool displayLocationPoints = true;
public bool displayFieldBoundaries = true;
public bool displayCityRoads = true;
public bool displayCityFields = true;
public bool displayTrees = true;
[NonSerialized] public float generationTime = 0.0f;
}
[Serializable]
public class LocationType
{
public string name;
public Color color;
public float height = 0.0f;
}
[ExecuteInEditMode]
public class LandmassGenerator : MonoBehaviour
{
[NonSerialized] public Map Map;
[NonSerialized] public Thread GenerationThread;
public GraphGeneratorDebug debug;
public Vector2 size = new Vector2();
public List<LocationType> types = new List<LocationType>();
[Range(0f, 360f)]
public float minimumRoadAngle = 30f;
[Range(0f, 4f)]
public float minimumNudgeDistance = 0.5f;
[Range(0f, 4f)]
public float maximumNudgeDistance = 1f;
[Range(0f, 4f)]
public float minimumRoadLength = 1f;
public float treeRadius = 2f;
public float forestRatio = .4f;
[Range(2.0f, 64.0f)]
public float radius = 8;
public int seed = Environment.TickCount;
public Graph<MapSite> SitesGraph => Map?.Sites;
public Graph<Point> BoundariesGraph => Map?.Boundaries;
public Graph<Location> LocationGraph => Map?.Metadata.GetProperty<Graph<Location>>(LandmassPass.MapLocationsProperty);
public void Generate(Action<Map> onDone)
{
GenerationThread?.Abort();
GenerationThread = new Thread(() =>
{
var generator = CreateMapGenerator();
var result = generator.Generate(size.x, size.y);
lock (GetType())
{
Map = result;
onDone(result);
}
});
GenerationThread.Start();
}
public void Generate()
{
Generate(_ => { });
}
public void OnDisable()
{
GenerationThread?.Abort();
}
private MapGenerator CreateMapGenerator()
{
var generator = new MapGenerator(new VoronoiGridGenerator(seed, new PoissonDiskSampler(radius) { Generator = new Random(seed) }));
generator.AddAnnotationPass(new LandmassPass(types.Prepend(new LocationType { name = "Ocean", height = -1 }).Select(t => new Location { Type = t }), new Random(seed)));
generator.AddAnnotationPass(new LocateCitiesPass(new Random(seed)));
generator.AddAnnotationPass(new CityFieldsPass(new Random(seed))
{
MinimumAngle = Mathf.Deg2Rad * minimumRoadAngle,
MaxNudgeDistance = maximumNudgeDistance,
MinNudgeDistance = minimumNudgeDistance,
MinimumRoadLength = minimumRoadLength
});
generator.AddAnnotationPass(new ForestPass
{
TreeRadius = treeRadius,
ForestRatio = forestRatio
});
return generator;
}
public void Reset()
{
GenerationThread?.Abort();
Map = null;
}
private void DisplayGraphCities(IEnumerable<City> cities)
{
foreach (City city in cities)
{
foreach (var (a, b) in city.Edges)
{
Gizmos.DrawLine(Map.Boundaries[a].ToVector3(), Map.Boundaries[b].ToVector3());
}
foreach (var a in city.Sites)
Gizmos.DrawSphere(a.Center.ToVector3(), 1);
}
}
void OnDrawGizmos()
{
if (!debug.enabled || Map == null) return;
if (debug.displayBoundary)
{
Gizmos.color = Color.gray;
Gizmos.DrawLine(new Vector3(0, 0, 0), new Vector3(size.x, 0, 0));
Gizmos.DrawLine(new Vector3(0, 0, 0), new Vector3(0, 0, size.y));
Gizmos.DrawLine(new Vector3(size.x, 0, size.y), new Vector3(size.x, 0, 0));
Gizmos.DrawLine(new Vector3(size.x, 0, size.y), new Vector3(0, 0, size.y));
}
if (debug.displayPoints)
{
Gizmos.color = Color.white;
DebugUtils.DisplayGraphVertices(Map.Sites, s => s.Center, displayLabels: debug.displayLabels);
}
if (debug.displayNeighbours)
{
Gizmos.color = Color.yellow;
DebugUtils.DisplayGraphEdges(Map.Sites, s => s.Center);
}
if (debug.displayVertices)
{
Gizmos.color = Color.blue;
DebugUtils.DisplayGraphVertices(Map.Boundaries, displayLabels: debug.displayLabels);
}
if (debug.displayEdges)
{
Gizmos.color = Color.white;
DebugUtils.DisplayGraphEdges(Map.Boundaries);
}
Gizmos.color = Color.blue;
if (!Map.Metadata.HasProperty(LandmassPass.MapLocationsProperty)) return;
var locations = Map.Metadata.GetProperty<Graph<Location>>(LandmassPass.MapLocationsProperty);
if (debug.displayLocationCells)
{
var cells = Map.Sites.Vertices
.Where(s => s.Metadata.HasProperty(LandmassPass.SiteLocationProperty))
.Where(s => s.Metadata.GetProperty<Location>(LandmassPass.SiteLocationProperty).Type.name != "Ocean");
foreach (var site in cells)
{
var location = site.Metadata.GetProperty<Location>(LandmassPass.SiteLocationProperty);
Gizmos.color = location.Type.color;
Gizmos.DrawSphere(site.Center.ToVector3(), 2);
}
}
//
// if (debug.displayLocations && _locationGraph != null)
// {
// DisplayGraphEdges(_locationGraph.Morph(a => a.Item1));
// }
//
// if (debug.displayLocationPoints)
// {
// foreach (var location in _locations.Skip(1))
// {
// Gizmos.color = location.Type.color;
// foreach (var point in location.BoundaryPoints)
// {
// var v = VoronoiGenerator.Voronoi.Vertices[point].ToVector3();
// Gizmos.DrawSphere(v, 1);
// if (debug.displayLabels) Handles.Label(v + Vector3.right, $"{point} at {v.x:F2}, {v.y:f2}");
// }
// }
// }
if (debug.displayLocationEdges)
{
foreach (var location in Map.Metadata.GetProperty<Graph<Location>>(LandmassPass.MapLocationsProperty).Vertices)
{
Gizmos.color = location.Type.color;
foreach (var (a, b) in location.BoundaryEdges)
{
Gizmos.DrawLine(BoundariesGraph[a].ToVector3(), BoundariesGraph[b].ToVector3());
}
}
}
if (debug.displayCities && Map.Metadata.HasProperty(LocateCitiesPass.CitiesProperty))
{
Gizmos.color = Color.magenta;
DisplayGraphCities(Map.Metadata.GetProperty<IEnumerable<City>>(LocateCitiesPass.CitiesProperty));
}
var cities = Map.Metadata.GetProperty<List<City>>(LocateCitiesPass.CitiesProperty);
if (debug.displayFieldBoundaries)
{
Gizmos.color = Color.white;
foreach (var city in cities)
{
DebugUtils.DisplayGraphEdges(city.FieldBoundaries);
DebugUtils.DisplayGraphVertices(city.Roads, displayLabels: debug.displayLabels);
}
}
if (debug.displayCityRoads)
{
Gizmos.color = Color.green;
foreach (var city in cities)
{
DebugUtils.DisplayGraphEdges(city.Roads);
DebugUtils.DisplayGraphVertices(city.Roads, displayLabels: debug.displayLabels);
}
}
if (debug.displayCityFields)
{
Gizmos.color = Color.green;
foreach (var city in cities)
{
foreach (var field in city.Fields)
{
foreach (var (a, b) in field.Boundary.RotateRight().Zip(field.Boundary, (a, b) => (a, b)))
Gizmos.DrawLine(a.ToVector3(), b.ToVector3());
foreach (var tuple in field.Boundary.Zip(PointUtils.CalculateNormals(field.Boundary), (a, b) => (Point: a, Normal: a + b)))
Gizmos.DrawLine(tuple.Point.ToVector3(), tuple.Normal.ToVector3());
}
}
}
if (debug.displayTrees)
{
Gizmos.color = Color.cyan;
var trees = Map.GetProperty<IEnumerable<Tree>>(ForestPass.TreesProperty);
foreach (var tree in trees)
Gizmos.DrawCube(
tree.Placement.ToVector3() + tree.Site.GetProperty<Location>(LandmassPass.SiteLocationProperty).Type.height * Vector3.up,
new Vector3(1, 1, 1)
);
}
}
}
}