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 types = new List(); [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 SitesGraph => Map?.Sites; public Graph BoundariesGraph => Map?.Boundaries; public Graph LocationGraph => Map?.Metadata.GetProperty>(LandmassPass.MapLocationsProperty); public void Generate(Action 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 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>(LandmassPass.MapLocationsProperty); if (debug.displayLocationCells) { var cells = Map.Sites.Vertices .Where(s => s.Metadata.HasProperty(LandmassPass.SiteLocationProperty)) .Where(s => s.Metadata.GetProperty(LandmassPass.SiteLocationProperty).Type.name != "Ocean"); foreach (var site in cells) { var location = site.Metadata.GetProperty(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>(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>(LocateCitiesPass.CitiesProperty)); } var cities = Map.Metadata.GetProperty>(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>(ForestPass.TreesProperty); foreach (var tree in trees) Gizmos.DrawCube( tree.Placement.ToVector3() + tree.Site.GetProperty(LandmassPass.SiteLocationProperty).Type.height * Vector3.up, new Vector3(1, 1, 1) ); } } } }