city-generation/Game/Assets/Scripts/Business/Generator/BuildingGenerator.cs

236 lines
8.4 KiB
C#
Raw Normal View History

2015-06-03 20:54:22 +00:00
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
{
2015-06-10 08:49:43 +00:00
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; } }
2015-06-03 20:54:22 +00:00
private Map map;
QuadTree<RoadNode> nodeTree;
QuadTree<BuildingLot> lotTree;
private void AllocateLots()
{
// Generate lots for every segment
foreach (var pair in map.RoadNetwork.ArticulationSegments)
{
2015-06-10 08:49:43 +00:00
bool didSomething;
var segment = pair.Value;
var dir = segment.Direction;
2015-06-03 20:54:22 +00:00
var perp = dir.RotateDeg(90);
2015-06-10 08:49:43 +00:00
float width0 = ConfigManager.Roadgen.SidewalkWidth + ConfigManager.Roadgen.LaneWidth * segment.LanesTo1;
float width1 = ConfigManager.Roadgen.SidewalkWidth + ConfigManager.Roadgen.LaneWidth * segment.LanesTo2;
2015-06-03 20:54:22 +00:00
2015-06-10 08:49:43 +00:00
Vector2 start = segment.Terminal1.Position;
Vector2 end = segment.Terminal2.Position;
Vector2 posL = start, posR = start, nposL, nposR;
int attempts = 0;
do
2015-06-03 20:54:22 +00:00
{
2015-06-10 08:49:43 +00:00
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);
2015-06-03 20:54:22 +00:00
}
// Done
map.BuildingLots = lotTree.ToList();
}
private bool CanAllocate(Vector2 pos, BuildingLot lot0)
{
2015-06-13 18:36:32 +00:00
if (lot0.Points.Any(p => !lotTree.Boundary.Contains(p)))
2015-06-10 08:49:43 +00:00
return false;
2015-06-03 20:54:22 +00:00
// Test other lots
2015-06-10 08:49:43 +00:00
Rectangle lotArea = new Rectangle(
pos.X - 2f * LotSquareMaxSize,
pos.Y - 2f * LotSquareMaxSize,
pos.X + 2f * LotSquareMaxSize,
pos.Y + 2f * LotSquareMaxSize);
2015-06-03 20:54:22 +00:00
foreach (var lot in lotTree.Query(lotArea))
{
2015-06-10 08:49:43 +00:00
if (BuildingLot.DoesIntersect(lot0, lot))
2015-06-03 20:54:22 +00:00
return false;
}
// Test nearby roads
Rectangle roadArea = new Rectangle(
2015-06-10 08:49:43 +00:00
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);
2015-06-03 20:54:22 +00:00
foreach (var node in nodeTree.Query(roadArea))
{
foreach (var segment in node.ArticulationSegments)
{
2015-06-10 08:49:43 +00:00
if (BuildingLot.DoesIntersect(lot0, segment.AsLineSegment()))
2015-06-03 20:54:22 +00:00
return false;
}
}
return true;
}
private void GenerateBuildings()
{
2015-06-10 08:49:43 +00:00
foreach (var lot in map.BuildingLots)
map.Buildings.Add(GenerateBuilding(lot));
}
Polygon GeneratePrimitivePolygon(BuildingLot lot)
{
2015-06-13 18:36:32 +00:00
List<Vector2> points = new List<Vector2>();
int sides = random.Next(4, 7); // Number of sides
2015-06-10 08:49:43 +00:00
float angle = 2 * (float)Math.PI / sides; // Angle between sides
2015-06-13 18:36:32 +00:00
bool ok;
2015-06-10 08:49:43 +00:00
2015-06-13 18:36:32 +00:00
do
{
// Reset
ok = true;
points.Clear();
2015-06-10 08:49:43 +00:00
2015-06-13 18:36:32 +00:00
// 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;
2015-06-10 08:49:43 +00:00
2015-06-13 18:36:32 +00:00
// Generate polygon
for (int i = 0; i < sides; i++)
{
points.Add(current);
2015-06-10 08:49:43 +00:00
2015-06-13 18:36:32 +00:00
// Make sure every point is inside
if (!lot.IsInside(current))
{
ok = false;
break;
}
current = current + dir;
dir = dir.Rotate(angle);
}
} while (!ok);
2015-06-10 08:49:43 +00:00
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<Polygon> polys = new List<Polygon>();
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
{
2015-06-13 18:36:32 +00:00
b.LevelHeights[i] = random.NextSingle(ConfigManager.Buildgen.MinBuildingHeight, ConfigManager.Buildgen.MaxBuildingHeight) * map.GetPopulation(lot.Position);
2015-06-10 08:49:43 +00:00
}
for (int j = 0; j < random.Next(ConfigManager.Buildgen.MaxPolygonsPerLevel); j++)
polys.Add(GeneratePrimitivePolygon(lot));
b.Polygons[i] = polys.ToArray();
}
2015-06-03 20:54:22 +00:00
2015-06-10 08:49:43 +00:00
return b;
2015-06-03 20:54:22 +00:00
}
public void Generate(Map map)
{
this.map = map;
2015-06-10 08:49:43 +00:00
map.Buildings = new List<Building>();
2015-06-03 20:54:22 +00:00
// Construct node tree
nodeTree = new QuadTree<RoadNode>(0, 0, map.Width, map.Height);
foreach (var pair in map.RoadNetwork.Nodes)
nodeTree.Add(pair.Value);
lotTree = new QuadTree<BuildingLot>(0, 0, map.Width, map.Height);
// Allocate lots
AllocateLots();
GenerateBuildings();
}
}
}