From ceb20fcba1b0d1ac8534b2bf67012fcbd8ce235f Mon Sep 17 00:00:00 2001 From: veniti Date: Tue, 10 Sep 2019 20:55:37 +0200 Subject: [PATCH] generating city fields --- Assets/Map/Graph.cs | 12 +- Assets/SampleScenes.meta | 8 ++ Assets/SampleScenes/Materials.meta | 8 ++ Assets/SampleScenes/Models.meta | 8 ++ Assets/SampleScenes/Models/Materials.meta | 8 ++ Assets/SampleScenes/Navmesh.meta | 8 ++ Assets/SampleScenes/Scenes.meta | 8 ++ .../Scenes/CharacterThirdPersonAI.meta | 8 ++ Assets/Scripts/CityGenerator.cs | 132 ++++++++++++------ Assets/Scripts/ForestGenerator.cs | 10 +- Assets/Scripts/MapRenderer.cs | 2 +- Assets/Voronoi/VoronoiGenerator.cs | 2 +- 12 files changed, 161 insertions(+), 53 deletions(-) create mode 100644 Assets/SampleScenes.meta create mode 100644 Assets/SampleScenes/Materials.meta create mode 100644 Assets/SampleScenes/Models.meta create mode 100644 Assets/SampleScenes/Models/Materials.meta create mode 100644 Assets/SampleScenes/Navmesh.meta create mode 100644 Assets/SampleScenes/Scenes.meta create mode 100644 Assets/SampleScenes/Scenes/CharacterThirdPersonAI.meta diff --git a/Assets/Map/Graph.cs b/Assets/Map/Graph.cs index b764f1c..438c012 100644 --- a/Assets/Map/Graph.cs +++ b/Assets/Map/Graph.cs @@ -6,7 +6,7 @@ using System.Linq; namespace Assets.Map { [Serializable] - public class Graph + public class Graph : ICloneable { public List Vertices { get; internal set; } = new List(); private Dictionary> _edges = new Dictionary>(); @@ -114,6 +114,11 @@ namespace Assets.Map return _edges[vertex]; } + + virtual public object Clone() + { + return Morph(l => l); + } } public class Graph : Graph @@ -153,5 +158,10 @@ namespace Assets.Map return result; } + + public override object Clone() + { + return Morph(l => l, l => l); + } } } \ No newline at end of file diff --git a/Assets/SampleScenes.meta b/Assets/SampleScenes.meta new file mode 100644 index 0000000..fadfba8 --- /dev/null +++ b/Assets/SampleScenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 3d8829ac873af27488b1412b8db301ab +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/SampleScenes/Materials.meta b/Assets/SampleScenes/Materials.meta new file mode 100644 index 0000000..32e392e --- /dev/null +++ b/Assets/SampleScenes/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 35e5b60069b1d4945af0cbaefb902d02 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/SampleScenes/Models.meta b/Assets/SampleScenes/Models.meta new file mode 100644 index 0000000..0ba62af --- /dev/null +++ b/Assets/SampleScenes/Models.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 92a62c583a2e8734d9a6018eec4d27fb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/SampleScenes/Models/Materials.meta b/Assets/SampleScenes/Models/Materials.meta new file mode 100644 index 0000000..40c3d80 --- /dev/null +++ b/Assets/SampleScenes/Models/Materials.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 7ff1b560cd403d249b3359b432be2796 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/SampleScenes/Navmesh.meta b/Assets/SampleScenes/Navmesh.meta new file mode 100644 index 0000000..2a621ba --- /dev/null +++ b/Assets/SampleScenes/Navmesh.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4014c75d332963b40a9724fb931da32e +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/SampleScenes/Scenes.meta b/Assets/SampleScenes/Scenes.meta new file mode 100644 index 0000000..eabad25 --- /dev/null +++ b/Assets/SampleScenes/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 26c0bbb8e6792b546bf28bbe08d7c96d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/SampleScenes/Scenes/CharacterThirdPersonAI.meta b/Assets/SampleScenes/Scenes/CharacterThirdPersonAI.meta new file mode 100644 index 0000000..7ff208b --- /dev/null +++ b/Assets/SampleScenes/Scenes/CharacterThirdPersonAI.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: c3ed31e3b158a9e4994bd4c23c947afb +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Scripts/CityGenerator.cs b/Assets/Scripts/CityGenerator.cs index 047225e..4b080ba 100644 --- a/Assets/Scripts/CityGenerator.cs +++ b/Assets/Scripts/CityGenerator.cs @@ -26,50 +26,65 @@ namespace Assets.Scripts public int GetHashCode(Point obj) { - return 0; + return obj.GetHashCode(); } } + public class CityField + { + public List boundary; + } + public class City { - public Graph startGraph { get; set; } - public List sitesList { get; set; } - public Graph roads { get; set; } = new Graph(); - public List<(int, int)> boundariesEdges { get; set; } - public Graph mainRoad { get; set; } = new Graph(); + public Graph startGraph; + public List sites; + public Graph roads = new Graph(); + public Graph fieldBoundaries = new Graph(); + + public List fields = new List(); + + public List<(int, int)> edges; + public City(Graph startGraph) { this.startGraph = startGraph; - this.sitesList = new List(); - this.boundariesEdges = new List<(int, int)>(); + + this.sites = new List(); + this.edges = new List<(int, int)>(); } public City(Graph startGraph, List sitesList) : this(startGraph) { - this.sitesList = sitesList; + this.sites = sitesList; } public void AddSite(Site site) { - sitesList.Add(site); + sites.Add(site); FixBoundaryEdges(site); } private void FixBoundaryEdges(Site site) { - var a = boundariesEdges; + var a = edges; var b = site.Edges.Select(x => x.Item1 < x.Item2 ? x : (x.Item2, x.Item1)); - boundariesEdges = a.Union(b).Except(a.Intersect(b)).ToList(); + edges = a.Union(b).Except(a.Intersect(b)).ToList(); } public void CreateRoadGraph(IList points) { - VoronoiGenerator generator = new VoronoiGenerator(sitesList.SelectMany(site => site.Vertices.Select(i => points[i]).Append(site.Point)).Distinct(new PointProximityComparer(2)).ToList()); + var pointsForRoads = sites.SelectMany(site => site.Vertices.Select(i => (i, points[i])).Append((-1, site.Point))).Distinct().ToList(); + var mapping = pointsForRoads.Select((x, i) => x.Item1 == -1 ? (-1, -1) : (i, x.Item1)).Where(x => x.Item1 != -1).ToDictionary(x => x.Item2, x => x.Item1); + + VoronoiGenerator generator = new VoronoiGenerator(pointsForRoads.Select(x => x.Item2)); generator.Generate(); - roads = generator.Delaunay.Morph(s => s.Point, e => Point.Dist(generator.Delaunay.Vertices[e.Item1].Point, generator.Delaunay.Vertices[e.Item2].Point)); + roads = generator.Delaunay.Morph(s => s.Point); + + fieldBoundaries = new Graph { Vertices = roads.Vertices, Edges = edges.Select(e => (mapping[e.Item1], mapping[e.Item2])) }; } } @@ -81,7 +96,7 @@ namespace Assets.Scripts public bool displayEdges = false; public bool displayCities = true; public bool displayCityRoads = true; - public bool displayCityMainRoads = true; + public bool displayFieldBoundaries = true; public bool displayWorldRoads = false; } @@ -95,9 +110,9 @@ namespace Assets.Scripts private List verticesForRoads; private List verticesForCitites; public GraphGeneratorDebug debug; - public City city; public List cities = new List(); - Graph roads = new Graph(); + Graph roads = new Graph(); + Graph mainRoads = new Graph(); List CountProbablitityOfCity(List vertices) @@ -115,6 +130,11 @@ namespace Assets.Scripts return ChoosePoints(20); } + public bool IsPointInsideCity(City city, Point point) + { + return city.sites.Any(s => PointUtils.IsPointInside(point, s.Vertices.Select(i => _voronoiGraph[i]).ToArray())); + } + City CreateCity(int vertex, int size) { City newCity = new City(_basicGraph); @@ -136,7 +156,20 @@ namespace Assets.Scripts } newCity.CreateRoadGraph(_voronoiGraph.Vertices); - newCity.mainRoad = ConnectPointsIntoRoads(newCity.roads); + + var edges = newCity.roads.Edges.ToList(); + foreach (var (a, b) in edges) + { + var (va, vb) = (newCity.roads[a], newCity.roads[b]); + var center = (va + vb) / 2; + + if (!IsPointInsideCity(newCity, center)) + newCity.roads.DeleteEdge(a, b); + } + + ConnectPointsIntoRoads(newCity); + CreateFieldBoundaries(newCity); + return newCity; } @@ -154,28 +187,35 @@ namespace Assets.Scripts return vertices; } - Graph ConnectPointsIntoRoads(Graph graph) + void ConnectPointsIntoRoads(City city) { + var original = city.roads.Morph(s => s, e => Point.Dist(city.roads[e.Item1], city.roads[e.Item2])); //1.Sort all the edges in non - decreasing order of their weight. //2.Pick the smallest edge.Check if it forms a cycle with the spanning tree formed so far. If cycle is not formed, include this edge.Else, discard it. //3.Repeat step#2 until there are (V-1) edges in the spanning tree. - graph.Edges = graph.Edges.OrderBy(e => graph.GetEdgeData(e)); - graph._edgeData = graph._edgeData.OrderByDescending(e => e.Value).ToDictionary(e => e.Key, k => k.Value); + var edges = original.Edges.OrderByDescending(e => original.GetEdgeData(e)); - Graph spanningTree = new Graph(); - spanningTree.Vertices = graph.Vertices; + Graph roads = new Graph() { Vertices = city.roads.Vertices }; - foreach (var edge in graph.Edges) + foreach (var edge in original.Edges) { - spanningTree.AddEdge(edge.Item1, edge.Item2); - if (Graph.HasCycle(spanningTree)) - spanningTree.DeleteEdge(edge.Item1, edge.Item2); - if (spanningTree.Edges.Count() == spanningTree.Vertices.Count() - 1) + roads.AddEdge(edge.Item1, edge.Item2); + + if (Graph.HasCycle(roads)) + roads.DeleteEdge(edge.Item1, edge.Item2); + if (roads.Edges.Count() == roads.Vertices.Count() - 1) break; } - return spanningTree; + + city.roads = roads; + } + + private void CreateFieldBoundaries(City city) + { + foreach (var (a, b) in city.roads.Edges) + city.fieldBoundaries.AddEdge(a, b); } void CreateWorldRoadGraph() @@ -250,10 +290,10 @@ namespace Assets.Scripts private void DisplayGraphCrossroads(Graph graph, List vertices) { - foreach (var v in vertices.Select(i => graph.Vertices[i].ToVector3())) - { - Gizmos.DrawSphere(v, 1); - } + //foreach (var v in vertices.Select(i => graph.Vertices[i].ToVector3())) + //{ + // Gizmos.DrawSphere(v, 1); + //} } @@ -262,12 +302,12 @@ namespace Assets.Scripts foreach (City city in cities) { Gizmos.color = Color.magenta; - foreach (var (a, b) in city.boundariesEdges) + foreach (var (a, b) in city.edges) { Gizmos.DrawLine(_voronoiGraph.Vertices[a].ToVector3(), _voronoiGraph.Vertices[b].ToVector3()); } - foreach (var a in city.sitesList) + foreach (var a in city.sites) Gizmos.DrawSphere(a.Point.ToVector3(), 1); } } @@ -281,6 +321,7 @@ namespace Assets.Scripts foreach (var (s, e) in edges) Gizmos.DrawLine(s, e); } + private void OnDrawGizmos() { @@ -308,9 +349,18 @@ namespace Assets.Scripts DisplayGraphCities(cities); } + if (debug.displayFieldBoundaries) + { + Gizmos.color = Color.white; + + foreach (var city in cities) + DisplayGraphEdges(city.fieldBoundaries); + } + if (debug.displayCityRoads) { - Gizmos.color = Color.blue; + Gizmos.color = Color.green; + foreach (var city in cities) { DisplayGraphEdges(city.roads); @@ -318,15 +368,7 @@ namespace Assets.Scripts } } - if (debug.displayCityMainRoads) - { - //Gizmos.color = new Color(2.54f, 0.127f, 1.56f); - Gizmos.color = Color.green; - foreach (var city in cities) - { - DisplayGraphEdges(city.mainRoad); - } - } + if (debug.displayWorldRoads) { // Gizmos.color = new Color(0.254f, 0.127f, 0.156f); diff --git a/Assets/Scripts/ForestGenerator.cs b/Assets/Scripts/ForestGenerator.cs index aa0fc42..fd326e1 100644 --- a/Assets/Scripts/ForestGenerator.cs +++ b/Assets/Scripts/ForestGenerator.cs @@ -21,7 +21,7 @@ namespace Assets public GameObject forest; private Random _random = new Random(); - private List _sites; + private List _sites; private GraphGenerator Generator => GetComponent(); @@ -39,17 +39,17 @@ namespace Assets private void PlaceTrees() { - foreach (LocationSite site in _sites) + foreach (Site site in _sites) { var sampler = new PoissonDiskSampler(radius) { Generator = _random }; Bounding bounding = - BoundingTools.GetBounding(site.Site.Vertices.Select(i => Generator.BoundariesGraph.Vertices[i])); + BoundingTools.GetBounding(site.Vertices.Select(i => Generator.BoundariesGraph.Vertices[i])); var offset = Vector3.up * site.Location.Type.height; var points = sampler.Generate((float)bounding.Width, (float)bounding.Height); - foreach (var point in points.Select(point => point + bounding.Min).Where(point => IsPointInSite(site.Site, point))) + foreach (var point in points.Select(point => point + bounding.Min).Where(point => IsPointInSite(site, point))) { PlaceRandomTree(point.ToVector3() + offset); } @@ -70,7 +70,7 @@ namespace Assets private void FindForestSites() { - _sites = new List(); + _sites = new List(); foreach (var location in Generator.LocationGraph.Vertices.Skip(1)) { diff --git a/Assets/Scripts/MapRenderer.cs b/Assets/Scripts/MapRenderer.cs index 6153ed3..8ae09f2 100644 --- a/Assets/Scripts/MapRenderer.cs +++ b/Assets/Scripts/MapRenderer.cs @@ -104,7 +104,7 @@ namespace Assets private void GenerateLocationMesh(Location location, IList points) { - foreach (var vertices in location.Sites.Select(site => site.Edges.Select(x => points[x.Item1]).Reverse())) + foreach (var vertices in location.Sites.Select(site => site.Edges.Select(x => x.Item1).Reverse())) { int start = _vertices.Count; diff --git a/Assets/Voronoi/VoronoiGenerator.cs b/Assets/Voronoi/VoronoiGenerator.cs index e17073c..9c6722b 100644 --- a/Assets/Voronoi/VoronoiGenerator.cs +++ b/Assets/Voronoi/VoronoiGenerator.cs @@ -59,7 +59,7 @@ namespace Assets.Voronoi public bool Done => Queue.Count == 0; - public VoronoiGenerator(IList sites) + public VoronoiGenerator(IEnumerable sites) { int i = 0; Sites = sites.Select(x => new Site(x, i++)).ToList();