inz-00/Assets/Scripts/GraphGenerator.cs

247 lines
8.2 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Assets.Common;
using Assets.Generators;
using Assets.Map;
using Assets.Voronoi;
using UnityEditor;
using UnityEngine;
using Random = System.Random;
namespace Assets
{
[Serializable]
public class GraphGeneratorDebug
{
public bool enabled = true;
public bool displayPoints = true;
public bool displayBeachLine = true;
public bool displayParabolas = false;
public bool displayVertices = true;
public bool displayNeighbours = false;
public bool displayLabels = false;
public bool displayHalfEdges = false;
public bool displayEdges = true;
public bool displayLocations = true;
}
[Serializable]
public class LocationType
{
public string name;
public Color color;
}
public enum GenerationStage
{
Start,
Voronoi,
Location,
Done
};
[RequireComponent(typeof(MeshRenderer), typeof(MeshFilter)), ExecuteInEditMode]
public class GraphGenerator : MonoBehaviour
{
private PoissonDiskSampler _sampler;
private Random _random;
private VoronoiGenerator _voronoiGenerator;
private LocationGenerator _locationGenerator;
private List<Vector3> _points = new List<Vector3>();
private List<Location> _locations = new List<Location>();
public List<LocationType> types = new List<LocationType>();
public VoronoiGenerator VoronoiGenerator => _voronoiGenerator;
public LocationGenerator LocationGenerator => _locationGenerator;
public int seed = Environment.TickCount;
public Vector2 size = new Vector2();
public GraphGeneratorDebug debug;
public GenerationStage Stage { get; private set; } = GenerationStage.Start;
[Range(2.0f, 64.0f)]
public float radius = 8;
private double _directrix = 0.0f;
void Start()
{
Reset();
}
public void Reset()
{
_random = new System.Random(seed);
_sampler = new PoissonDiskSampler(radius);
_sampler.Generator = _random;
_points = new List<Vector3>();
_points.Add(new Vector3(-size.x/8, size.y / 2 + 0.1f));
_points.Add(new Vector3(size.x * 1.125f, size.y / 2 - 0.1f));
_points.Add(new Vector3(size.x / 2 - 0.1f, -size.y/8));
_points.Add(new Vector3(size.x / 2 + 0.1f, size.y * 1.125f));
_points.AddRange(_sampler.Generate(size.x, size.y));
Stage = GenerationStage.Voronoi;
_voronoiGenerator = new VoronoiGenerator(_points.Select(x => new Point(x.x, x.y)).ToList());
_locationGenerator = null;
_locations = new List<Location>() { new Location() { Type = new LocationType() { color = Color.clear, name = "Ocean" } } };
_locations.AddRange(types.Select(type => new Location { Type = type }));
}
public void Generate()
{
while (Stage != GenerationStage.Done)
{
Step();
}
}
public void Step()
{
if (Stage == GenerationStage.Voronoi)
{
_voronoiGenerator.Step();
_directrix = _voronoiGenerator.Line.Directrix;
if (_voronoiGenerator.Done)
{
Stage = GenerationStage.Location;
_locationGenerator = new LocationGenerator(_voronoiGenerator.Delaunay.Morph(site => new LocationSite(site)), _random);
_locationGenerator.Init(
_locations.Select(location => (_random.Next(_voronoiGenerator.Delaunay.Vertices.Count), location))
.Concat(new List<int> { 0, 1, 2, 3 }.Select(i => (i, _locations[0]))) // borders should be water
.ToArray()
);
}
}
else if (Stage == GenerationStage.Location)
{
_locationGenerator.Step();
if (_locationGenerator.Done) Stage = GenerationStage.Done;
}
}
void OnDrawGizmos()
{
if (!debug.enabled || _voronoiGenerator == null) return;
if (debug.displayPoints)
{
Gizmos.color = Color.white;
DisplayGraphVertices(_voronoiGenerator.Delaunay.Morph(x => x.Point));
}
if (debug.displayNeighbours)
{
Gizmos.color = Color.yellow;
DisplayGraphEdges(_voronoiGenerator.Delaunay.Morph(x => x.Point));
}
if (debug.displayVertices)
{
Gizmos.color = Color.blue;
DisplayGraphVertices(_voronoiGenerator.Voronoi);
}
if (debug.displayHalfEdges)
{
Gizmos.color = Color.green;
foreach (var edge in _voronoiGenerator.HalfEdges)
{
Gizmos.DrawLine(edge.Start.ToVector3(), edge.End.ToVector3());
}
}
if (debug.displayEdges)
{
Gizmos.color = Color.white;
DisplayGraphEdges(_voronoiGenerator.Voronoi);
}
Gizmos.color = Color.red;
Gizmos.DrawLine(new Vector3(0, (float)_voronoiGenerator.Line.Directrix), new Vector3(size.x, (float)_voronoiGenerator.Line.Directrix));
Gizmos.color = Color.blue;
Vector3 start;
if (debug.displayParabolas)
{
foreach (var parabola in _voronoiGenerator.Line.Parabolas)
{
start = new Vector3(-size.x, (float)_voronoiGenerator.Line.EvalParabola(parabola.Site.Point, -size.x));
for (var x = -size.x + 0.1f; x < size.x * 2; x += 0.1f)
{
var point = new Vector3(x, (float)_voronoiGenerator.Line.EvalParabola(parabola.Site.Point, x));
Gizmos.DrawLine(start, point);
start = point;
}
}
}
if (debug.displayBeachLine)
{
Gizmos.color = Color.white;
start = new Vector3(-size.x, (float)_voronoiGenerator.Line.Eval(-size.x));
for (var x = -size.x + 0.1f; x < size.x * 2; x += 0.1f)
{
var point = new Vector3(x, (float)_voronoiGenerator.Line.Eval(x));
Gizmos.DrawLine(start, point);
start = point;
}
}
if (debug.displayLocations && LocationGenerator != null)
{
foreach (var location in LocationGenerator.Result.Vertices.Where(l => l.Location != null && l.Location != _locations[0]))
{
Gizmos.color = location.Location.Type.color;
Gizmos.DrawSphere(location.Site.Point.ToVector3(), 2);
}
}
}
public void Rewind(float y)
{
while (_voronoiGenerator.Line.Directrix < y && !_voronoiGenerator.Done)
{
_voronoiGenerator.Step();
}
}
private void DisplayGraphVertices(Graph<Point> graph)
{
var vertices = graph.Vertices.Select(p => p.ToVector3());
var offset = Vector3.right;
foreach (var (v, i) in vertices.Select((x, i) => (x, i)))
{
Gizmos.DrawSphere(v, 1);
if (debug.displayLabels) Handles.Label(v + offset, $"{i}");
}
}
private void DisplayGraphEdges(Graph<Point> graph)
{
var edges = graph.Edges
.Select(edge => (graph.Vertices[edge.Item1], graph.Vertices[edge.Item2]))
.Select(edge => (edge.Item1.ToVector3(), edge.Item2.ToVector3()));
foreach (var (s, e) in edges) Gizmos.DrawLine(s, e);
}
}
}