305 lines
11 KiB
C#
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)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
} |