236 lines
8.5 KiB
C#
236 lines
8.5 KiB
C#
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<RoadNode> nodeTree;
|
|
QuadTree<BuildingLot> 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<Vector2> points = new List<Vector2>();
|
|
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<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
|
|
{
|
|
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<Building>();
|
|
|
|
// 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();
|
|
}
|
|
}
|
|
}
|