/// <summary> /// Create a Voronoi Diagram using a list of points and a specified algorithm to use /// </summary> public static VoronoiDiagram CreateVoronoi(List <Point> points, GenerationSettings settings) { VoronoiDiagram voronoi; var startX = settings.StartX; var startY = settings.StartX; var width = settings.Width; var length = settings.Length; //Select algorithm to use switch (settings.VoronoiAlgorithm) { // Voronoi according to Boywer-Watson Algorithm // http://paulbourke.net/papers/triangulate/ case VoronoiAlgorithm.BoywerWatson: { voronoi = new BowyerWatsonGenerator().GetVoronoi(points); break; } // Voronoi according to Fortunes Algorithm // http://blog.ivank.net/fortunes-algorithm-and-implementation.html case VoronoiAlgorithm.Fortune: { voronoi = new FortuneGenerator().GetVoronoi(points); break; } default: throw new ArgumentOutOfRangeException(nameof(settings.VoronoiAlgorithm), settings.VoronoiAlgorithm, null); } voronoi.Bounds = new Rectangle(startX, startY, width, length); voronoi.Sites = points; voronoi.FinishVoronoi(); return(voronoi);; }
/// <summary> /// Create a Voronoi Diagram using a list of points and a specified algorithm to use /// </summary> public static VoronoiDiagram CreateVoronoi(List<Point> points, GenerationSettings settings) { VoronoiDiagram voronoi; var startX = settings.StartX; var startY = settings.StartX; var width = settings.Width; var length = settings.Length; //Select algorithm to use switch (settings.VoronoiAlgorithm) { // Voronoi according to Boywer-Watson Algorithm // http://paulbourke.net/papers/triangulate/ case VoronoiAlgorithm.BoywerWatson: { voronoi = new BowyerWatsonGenerator().GetVoronoi(points); break; } // Voronoi according to Fortunes Algorithm // http://blog.ivank.net/fortunes-algorithm-and-implementation.html case VoronoiAlgorithm.Fortune: { voronoi = new FortuneGenerator().GetVoronoi(points); break; } default: throw new ArgumentOutOfRangeException(nameof(settings.VoronoiAlgorithm), settings.VoronoiAlgorithm, null); } voronoi.Bounds = new Rectangle(startX, startY, width, length); voronoi.Sites = points; voronoi.FinishVoronoi(); return voronoi;; }
/// <summary> /// Generate points and store them into a list /// </summary> private void GeneratePoints(int amount) { //clear previous data _points.Clear(); _voronoiDiagram.Clear(); _cityData.Clear(); //fill settings _voronoiSettings = new GenerationSettings { VoronoiAlgorithm = VoronoiAlgorithm, PointAlgorithm = PointAlgorithm, Width = Width, Length = Height, StartX = StartX, StartY = StartY, UseSeed = UseSeed, Seed = Seed, Amount = PointsToGenerate, CircleRadius = Radius }; //generate points var timer = Stopwatch.StartNew(); _points = PointGenerator.Generate(_voronoiSettings); timer.Stop(); //update generation timer var time = timer.ElapsedMilliseconds/1000.0; GenerationTimeText = $"Generated {_points.Count} points in {time} seconds."; //update randomly generated seed if (UseSeed == false) Seed = _voronoiSettings.Seed; //update canvas RefreshCanvas(); }
private static List<Point> CircleDistribution(GenerationSettings settings) { var points = new List<Point>(); for (int i = 0; i < settings.Amount; i++) { var origin = new Point(settings.Width/2, settings.Length/2); var p = MathHelpers.GenerateRandomPointInCircle(origin, settings.CircleRadius); points.Add(p); } return points; }
private static List<Point> UniformDistribution(GenerationSettings settings) { var points = new List<Point>(); var width = settings.Width; var length = settings.Length; int gran = 20000; int widthM = 0, lengthM = 0; if (width > length) { lengthM = gran; widthM = (int)Math.Floor(gran*width/length); } else { widthM = gran; lengthM = (int)Math.Floor(gran * length / width); } for (int i = 0; i < settings.Amount; i++) { var p = Point.Zero; p.X = width*RandomHelper.RandomInt((int)settings.StartX, widthM)/widthM; p.Y = length * RandomHelper.RandomInt((int)settings.StartY, lengthM) / lengthM; points.Add(p); } return points; }
public static List<Point> Generate(GenerationSettings settings) { List<Point> generatedPoints; var startX = settings.StartX; var startY = settings.StartY; var width = settings.Width; var length = settings.Length; var amount = settings.Amount; // Seed random var seed = settings.UseSeed ? settings.Seed : DateTime.Now.GetHashCode(); _rng = new Random(seed); //generate points according to chosen algorithm switch (settings.PointAlgorithm) { case PointGenerationAlgorithm.Simple: generatedPoints = SimpleSpread(settings); break; case PointGenerationAlgorithm.CityLike: generatedPoints = CityLikeSpread(settings); break; case PointGenerationAlgorithm.Uniform: generatedPoints = UniformDistribution(settings); break; case PointGenerationAlgorithm.Circle: generatedPoints = CircleDistribution(settings); break; default: throw new ArgumentOutOfRangeException(); } //generatedPoints = new List<Point>(); generatedPoints.FilterDoubleValues(); if (settings.PointAlgorithm != PointGenerationAlgorithm.Circle) { //Add Bounds as points var bounds = new Point(startX, startY); generatedPoints.Add(bounds); generatedPoints.Add(new Point(bounds.X + width, bounds.Y)); generatedPoints.Add(new Point(bounds.X, bounds.Y + length)); generatedPoints.Add(new Point(bounds.X + width, bounds.Y + length)); var halfWidth = width/2; var halfLength = length/2; //Add half bounds as points generatedPoints.Add(new Point(bounds.X + halfWidth, bounds.Y)); generatedPoints.Add(new Point(bounds.X + halfWidth, bounds.Y + length)); generatedPoints.Add(new Point(bounds.X, bounds.Y + halfLength)); generatedPoints.Add(new Point(bounds.X + width, bounds.Y + halfLength)); } //restore back to original settings settings.StartX = startX; settings.StartY = startY; settings.Width = width; settings.Amount = amount; settings.Length = length; return generatedPoints; }
private static List<Point> CityLikeSpread(GenerationSettings settings) { var startX = (int)settings.StartX; var startY = (int)settings.StartY; var width = (int)settings.Width; var length = (int)settings.Length; var offset = 0.20; //amount of points will be divided over both generations settings.Amount = (int)Math.Floor(settings.Amount * 0.3); //generate outer points var points = SimpleSpread(settings); //calculate new start point and bounds settings.StartX = startX + (width * offset); settings.StartY = startY + (length * offset); offset *= 2; settings.Width = width * (1-offset); settings.Length = length * (1-offset); //generate inner points settings.Amount = (int)Math.Floor(settings.Amount * 0.7); points.AddRange(SimpleSpread(settings)); return points; }
private static List<Point> SimpleSpread(GenerationSettings settings) { //Create point list var points = new List<Point>(); var startX = (int)settings.StartX; var startY = (int)settings.StartY; var width = (int)settings.Width; var length = (int)settings.Length; //Generate points and add them to the collection for (var i = 0; i < settings.Amount; ++i) { var x = _rng.Next(startX, startX + width); var y = _rng.Next(startY, startY + length); var point = new Point(x, y); points.Add(point); } return points; }
/// <summary> /// set required data for generating the terrain /// </summary> public void InitializeSettings(TerrainSettings terrainSettings, GenerationSettings voronoiSettings, GameObject parent, CitySettings citySettings) { //store settings _terrainSettings = terrainSettings; _voronoiSettings = voronoiSettings; _citySettings = citySettings; _parentGameObject = parent; //initialize noise //if no seed is specified generate a random one if (!terrainSettings.UseSeed) { _terrainSettings.GroundSeed = Random.Range(int.MinValue, int.MaxValue); _terrainSettings.MountainSeed = Random.Range(int.MinValue, int.MaxValue); _terrainSettings.TreeSeed = Random.Range(int.MinValue, int.MaxValue); _terrainSettings.DetailSeed = Random.Range(int.MinValue, int.MaxValue); } //create noise _groundNoise = new PerlinNoise(_terrainSettings.GroundSeed); _treeNoise = new PerlinNoise(_terrainSettings.TreeSeed); _detailNoise = new PerlinNoise(_terrainSettings.DetailSeed); //calculate total size of 1 terrain tile based on the city bounds _terrainSize = (int)(_voronoiSettings.Width * 2f); _grassLayers = 0; _meshLayers = 0; //create the prototypes used by the generator CreatePrototypes(); }
/// <summary> /// Initialize base settings /// </summary> public void Initialize() { //create a town generator object when it doesn't exist yet if (_townGenerator != null) return; //Access towngenerator _townGenerator = TownGenerator.GetInstance(); //Set default settings _generationSettings = new GenerationSettings { VoronoiAlgorithm = VoronoiAlgorithm.Fortune, PointAlgorithm = PointGenerationAlgorithm.CityLike }; //City settings _citySettings = new CitySettings(); _citySettings.DebugMode = false; //Create a base district type CreatePrefabSelection("Grass"); //Terrain settings editor _terrainEditor = new TerrainEditor(new TerrainSettings(), this); //Define a style for the foldout _foldoutStyle = new GUIStyle(EditorStyles.foldout) { fontStyle = FontStyle.Bold, }; var c = Color.black; _foldoutStyle.onActive.textColor = c; _foldoutStyle.normal.textColor = c; _foldoutStyle.onNormal.textColor = c; _foldoutStyle.hover.textColor = c; _foldoutStyle.onHover.textColor = c; _foldoutStyle.focused.textColor = c; _foldoutStyle.onFocused.textColor = c; _foldoutStyle.active.textColor = c; _foldoutStyle.onActive.textColor = c; }
private void LoadFromXml(string filename) { //Generation settings _generationSettings = new GenerationSettings(); var root = XElement.Load(filename); //locate generation node var generation = root.Element("Generation"); var district = root.Element("Districts"); var terrain = root.Element("Terrain"); if (generation == null) return; _generationSettings.UseSeed = bool.Parse(generation.Element("UseSeed").Value); _generationSettings.Seed = int.Parse(generation.Element("Seed").Value); _generationSettings.Width = double.Parse(generation.Element("Width").Value); _generationSettings.Length = double.Parse(generation.Element("Length").Value); _generationSettings.StartX = double.Parse(generation.Element("StartX").Value); _generationSettings.StartY = double.Parse(generation.Element("StartY").Value); _generationSettings.Amount = int.Parse(generation.Element("Amount").Value); _generationSettings.PointAlgorithm = (PointGenerationAlgorithm) Enum.Parse(typeof(PointGenerationAlgorithm), generation.Element("Point").Value); _generationSettings.VoronoiAlgorithm = (VoronoiAlgorithm)Enum.Parse(typeof(VoronoiAlgorithm), generation.Element("Voronoi").Value); var parentname = generation.Element("Parent").Value; _townGenerator.Parent = null; if (parentname != string.Empty) { _townGenerator.Parent = GameObject.Find(parentname); } //Load in terrain settings var splatmaps = new List<SplatTexture>(); foreach (var e in terrain.Element("Splatmaps").Elements()) { var newSplat = new SplatTexture { Texture = AssetDatabase.LoadAssetAtPath<Texture2D>(e.Value), TileSize = float.Parse(e.Attribute("Tiling").Value) }; splatmaps.Add(newSplat); } var trees = new List<GameObject>(); foreach (var e in terrain.Elements("Trees").Elements()) { var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(e.Value); trees.Add(prefab); } var props = new List<GameObject>(); foreach (var e in terrain.Elements("Props").Elements()) { var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(e.Value); props.Add(prefab); } var details = new List<DetailObject>(); foreach (var e in terrain.Elements("Details").Elements()) { var d = new DetailObject(); var type = (DetailType)int.Parse(e.Attribute("Type").Value); switch (type) { case DetailType.Texture: d.Detail = AssetDatabase.LoadAssetAtPath<Texture2D>(e.Value); d.Type = DetailType.Texture; break; case DetailType.GameObject: d.Detail = AssetDatabase.LoadAssetAtPath<GameObject>(e.Value); d.Type = DetailType.GameObject; break; } details.Add(d); } //lakes _terrainEditor.Settings.GenerateLake = bool.Parse(terrain.Element("Lakes").Attribute("generate").Value); _terrainEditor.Settings.WaterPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(terrain.Element("Lakes").Element("lake").Value); _terrainEditor.GetSettings().SplatMaps = splatmaps; _terrainEditor.GetSettings().Trees = trees; _terrainEditor.GetSettings().Props = props; _terrainEditor.GetSettings().Details = details; //Clear previous loaded districts _prefabSelectors.Clear(); foreach (var districtElem in district.Elements()) { //Create district var distictEditor = CreatePrefabSelection(districtElem.Name.ToString()); if (distictEditor != null) { distictEditor.GetSettings().Frequency = int.Parse(districtElem.Element("Frequency").Value); distictEditor.GetSettings().Size = double.Parse(districtElem.Element("Size").Value); distictEditor.GetSettings().Offset = int.Parse(districtElem.Element("Offset").Value); distictEditor.GetSettings().Percentage = double.Parse(districtElem.Element("Interval").Value); //Load in all buildings distictEditor.ResetPrefabs(); foreach (var bElem in districtElem.Element("Buildings").Elements()) { distictEditor.AddPrefab(bElem.Value); } } } }
/// <summary> /// Define preset settings based on a type /// </summary> private GenerationSettings GetSettingsFromPreset(CityType cityType) { var genSettings = new GenerationSettings(); switch (cityType) { //Small case CityType.Village: { genSettings.Width = 150; genSettings.Length = 150; genSettings.PointAlgorithm = PointGenerationAlgorithm.Simple; genSettings.Amount = 20; _citySettings.GenerateInnerRoads = false; break; } //medium case CityType.Town: { genSettings.Width = 200; genSettings.Length = 200; genSettings.PointAlgorithm = PointGenerationAlgorithm.Simple; genSettings.Amount = 50; _citySettings.GenerateInnerRoads = true; break; } //large case CityType.Metropolis: { genSettings.Width = 350; genSettings.Length = 350; genSettings.PointAlgorithm = PointGenerationAlgorithm.CityLike; genSettings.Amount = 100; break; } default: break; } //still allow seed to be used with presets genSettings.UseSeed = _generationSettings.UseSeed; genSettings.Seed = _generationSettings.Seed; //prefer fortune algorithm for speed genSettings.VoronoiAlgorithm = VoronoiAlgorithm.Fortune; return genSettings; }
/// <summary> /// The buttons for generating, building and resetting /// </summary> private void GenerationButtons() { GUILayout.BeginHorizontal(); //Generate a terrain and a voronoi diagram on the terrain if (GUILayout.Button("Generate")) { //Use custom settings or a preset _generationSettings = _showAdvancedSettings ? _generationSettings : GetSettingsFromPreset(_cityType); //Store district information _citySettings.DistrictSettings = MakeDistrictSettings(); _townGenerator.PrefabsPerZone = MakePrefabList(); _townGenerator.Generate(_generationSettings, _citySettings, _terrainEditor.GetSettings()); } ////Using the voronoi data, create a city and build it //if (GUILayout.Button("Build")) //{ // _townGenerator.Build(); //} //Clear all generated data if (GUILayout.Button("Clear")) { _townGenerator.Clear(); } GUILayout.EndHorizontal(); }