using System; using System.Collections.Generic; using System.Linq; using System.Text; using TransportGame.Business; using TransportGame.Model; using TransportGame.Model.Road; using TransportGame.Utils; namespace TransportGame.Generator { public class BuildingGenerator { Random random = new Random(); private float LotSquareMinSize { get { return ConfigManager.Buildgen.LotSquareMinSize; } } private float LotSquareMaxSize { get { return ConfigManager.Buildgen.LotSquareMaxSize; } } private float LotSpacing { get { return ConfigManager.Buildgen.LotSpacing; } } private Map map; QuadTree nodeTree; QuadTree lotTree; private void AllocateLots() { // Generate lots for every segment foreach (var pair in map.RoadNetwork.ArticulationSegments) { bool didSomething; var segment = pair.Value; var dir = segment.Direction; var perp = dir.RotateDeg(90); float width0 = ConfigManager.Roadgen.SidewalkWidth + ConfigManager.Roadgen.LaneWidth * segment.LanesTo1; float width1 = ConfigManager.Roadgen.SidewalkWidth + ConfigManager.Roadgen.LaneWidth * segment.LanesTo2; Vector2 start = segment.Terminal1.Position; Vector2 end = segment.Terminal2.Position; Vector2 posL = start, posR = start, nposL, nposR; int attempts = 0; do { didSomething = false; float sizeL = random.NextSingle(LotSquareMinSize, LotSquareMaxSize), sizeR = random.NextSingle(LotSquareMinSize, LotSquareMaxSize); nposL = posL + dir * sizeL; nposR = posR + dir * sizeR; // Left side if ((posL - end).LengthSq >= sizeL * sizeL) { didSomething = true; // Build lot squares Vector2[] left = new Vector2[4]; left[0] = posL + perp * (width0 + LotSpacing + sizeL); left[1] = posL + perp * (width0 + LotSpacing); left[2] = nposL + perp * (width0 + LotSpacing); left[3] = nposL + perp * (width0 + LotSpacing + sizeL); BuildingLot lot = new BuildingLot(sizeL, left); if (CanAllocate(posL, lot)) lotTree.Add(lot); // Advance posL = nposL; } // Right side if ((posR - end).LengthSq >= sizeR * sizeR) { didSomething = true; // Build lot squares Vector2[] right = new Vector2[4]; right[0] = posR - perp * (width1 + LotSpacing + sizeR); right[1] = posR - perp * (width1 + LotSpacing); right[2] = nposR - perp * (width1 + LotSpacing); right[3] = nposR - perp * (width1 + LotSpacing + sizeR); BuildingLot lot = new BuildingLot(sizeR, right); if (CanAllocate(posL, lot)) lotTree.Add(lot); // Advance posR = nposR; } if (!didSomething) attempts++; } while (attempts < ConfigManager.Buildgen.MaxLotAttempts); } // Done map.BuildingLots = lotTree.ToList(); } private bool CanAllocate(Vector2 pos, BuildingLot lot0) { if (lot0.Points.Any(p => !lotTree.Boundary.Contains(p))) return false; // Test other lots Rectangle lotArea = new Rectangle( pos.X - 2f * LotSquareMaxSize, pos.Y - 2f * LotSquareMaxSize, pos.X + 2f * LotSquareMaxSize, pos.Y + 2f * LotSquareMaxSize); foreach (var lot in lotTree.Query(lotArea)) { if (BuildingLot.DoesIntersect(lot0, lot)) return false; } // Test nearby roads Rectangle roadArea = new Rectangle( pos.X - 1.1f * ConfigManager.Roadgen.HighwaySegmentLength, pos.Y - 1.1f * ConfigManager.Roadgen.HighwaySegmentLength, pos.X + 1.1f * ConfigManager.Roadgen.HighwaySegmentLength, pos.Y + 1.1f * ConfigManager.Roadgen.HighwaySegmentLength); foreach (var node in nodeTree.Query(roadArea)) { foreach (var segment in node.ArticulationSegments) { if (BuildingLot.DoesIntersect(lot0, segment.AsLineSegment())) return false; } } return true; } private void GenerateBuildings() { foreach (var lot in map.BuildingLots) map.Buildings.Add(GenerateBuilding(lot)); } Polygon GeneratePrimitivePolygon(BuildingLot lot) { List points = new List(); int sides = random.Next(4, 7); // Number of sides float angle = 2 * (float)Math.PI / sides; // Angle between sides bool ok; do { // Reset ok = true; points.Clear(); // Generate radius, start position and direction float radius = random.NextSingle(lot.Size * 0.25f, lot.Size * 0.5f); // Length of a side Vector2 current = lot.Position + new Vector2(random.NextSingle(-lot.Size / 2, lot.Size / 2), random.NextSingle(-lot.Size / 2, lot.Size / 2)); Vector2 dir = new Vector2(random.NextSingle(), random.NextSingle()).Normalized * radius; // Generate polygon for (int i = 0; i < sides; i++) { points.Add(current); // Make sure every point is inside if (!lot.IsInside(current)) { ok = false; break; } current = current + dir; dir = dir.Rotate(angle); } } while (!ok); return new Polygon(points); } private Building GenerateBuilding(BuildingLot lot) { Building b = new Building(); int levelCount = random.Next(1, ConfigManager.Buildgen.MaxBuildingLevels); b.LevelHeights = new float[levelCount]; b.Polygons = new Polygon[levelCount][]; for (int i = levelCount - 1; i >= 0; --i) { List polys = new List(); for (int j = 0; j < 1 + random.Next(ConfigManager.Buildgen.MaxPolygonsPerLevel); j++) polys.Add(GeneratePrimitivePolygon(lot)); if (i + 1 < levelCount) { polys.AddRange(b.Polygons[i + 1]); b.LevelHeights[i] = random.NextSingle(0, b.LevelHeights[i + 1]); } else { b.LevelHeights[i] = ConfigManager.Buildgen.MinLevelHeight + (random.NextSingle() * map.GetPopulation(lot.Position)) * (ConfigManager.Buildgen.MaxLevelHeight - ConfigManager.Buildgen.MinLevelHeight); } for (int j = 0; j < random.Next(ConfigManager.Buildgen.MaxPolygonsPerLevel); j++) polys.Add(GeneratePrimitivePolygon(lot)); b.Polygons[i] = polys.ToArray(); } return b; } public void Generate(Map map) { this.map = map; map.Buildings = new List(); // Construct node tree nodeTree = new QuadTree(0, 0, map.Width, map.Height); foreach (var pair in map.RoadNetwork.Nodes) nodeTree.Add(pair.Value); lotTree = new QuadTree(0, 0, map.Width, map.Height); // Allocate lots AllocateLots(); GenerateBuildings(); } } }