Generating Buildings – Medieval City Example

This page will detail how to generate a small medieval city that can be seen in the BuildR examples folder BuildR2\Examples under the Unity scene Carcassonne.unity. At the very bottom I have dumped the whole code example if you just want to take a look at that. I will however try to go through it. This example scene though is an ongoing project and will be modified. The current version of BuildR is 2.17 released in early January 2018.

The basic premise of this generator is to create a city based using a Voronoi diagram to generate its internal road structure and building plots. The plots are then subdivided using a basic oriented bounding box method. Some plots are left to be open spaces. The roads are currently rendered as simple untextured planes. The buildings are fully rendered from a selection of pre-made facade designs.

1
2
3
4
5
6
7
_rGen = new RandomGen(seed);
Rectf map = new Rectf(0, 0, mapSize, mapSize);
List<Vector2f> points = new List<Vector2f>();
for (int i = 0; i < voronoiCount; i++)
    points.Add(new Vector2f(_rGen.Range(0, mapSize), _rGen.Range(0, mapSize)));
Voronoi v = new Voronoi(points, map);
v.LloydRelaxation(1);

The initial part generates a Voronoi graph based on randomly selected points within a specified size. The points are relaxed once to create a more even spacing and remove any extreme values. The plots generated will therefore be more uniform but will retain some decent chaos.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
            List<List<Vector2f>> vQuarters = v.Regions();
            int numberOfQuarters = vQuarters.Count;
            for (int r = 0; r < numberOfQuarters; r++)
            {
                List<Vector2f> vQuarter = vQuarters[r];
                int quarterSize = vQuarter.Count;

                CarcassonneQuarter newQuarter = new CarcassonneQuarter();
                quarters.Add(newQuarter);
                for (int s = 0; s < quarterSize; s++)
                {
                    Vector2Int vip = new Vector2Int(vQuarter[s].x, vQuarter[s].y);
                    newQuarter.points.Add(vip);
                    if (!pointQuarters.ContainsKey(vip))
                        pointQuarters.Add(vip, new List<CarcassonneQuarter>());
                    pointQuarters[vip].Add(newQuarter);
                    if (!newQuarter.externalQuarter && PointOnEdge(vip)) newQuarter.externalQuarter = true;//
                }
                newQuarter.CalculateCenter();
            }

The Voronoi graph is then iterated over to turn it into something that can be used for the city generation. Each cell is converted into a CarcassonneQuarter (think of a quarter as being a medieval block of buildings. Each side will be flanked with a road). The quarters that touch the edge of the Voronoi graph are marked external. These will be ignored for the city generation and will give rise to a city that is more organically shaped along its boundary. BuildR uses Vector2Int which can be more easily compared against each other.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
for (int q = 0; q < numberOfQuarters; q++)
            {
                CarcassonneQuarter quarter = quarters[q];
                int quarterSize = quarter.points.Count;
                for (int s = 0; s < quarterSize; s++)
                {
                    Vector2Int vip = quarter.points[s];
                    int indexB = s < quarterSize - 1 ? s + 1 : 0;
                    int indexC = s > 0 ? s - 1 : quarterSize - 1;
                    Vector2Int vipb = quarter.points[indexB];
                    Vector2Int vipc = quarter.points[indexC];

                    //Link the road points up to create the network
                    CarcassonneRoadNetworkPoint roadNode;
                    if (!roadNodes.ContainsKey(vip))
                    {
                        roadNode = new CarcassonneRoadNetworkPoint();
                        roadNode.position = vip;
                        roadNodes.Add(vip, roadNode);
                        nodes.Add(roadNode);
                    }
                    else
                    {
                        roadNode = roadNodes[vip];
                    }

                    if (roadNodes.ContainsKey(vipb))
                    {
                        CarcassonneRoadNetworkPoint roadNodeb = roadNodes[vipb];
                        if (!roadNode.link.Contains(roadNodeb))
                        {
                            roadNode.link.Add(roadNodeb);
                            roadNodeb.link.Add(roadNode);

                            CarcassonneRoad newRoad = new CarcassonneRoad();
                            CarcassonneQuarter[] connectedQuarters = GetQuarters(vip, vipb);
                            newRoad.pointA = vip;
                            newRoad.pointB = vipb;
                            newRoad.quarterA = connectedQuarters[0];
                            newRoad.quarterB = connectedQuarters[1];

                            if (connectedQuarters[0] != null) connectedQuarters[0].roads.Add(newRoad);
                            if (connectedQuarters[1] != null) connectedQuarters[1].roads.Add(newRoad);

                            roads.Add(newRoad);
                        }
                    }

                    if (roadNodes.ContainsKey(vipc))
                    {
                        CarcassonneRoadNetworkPoint roadNodec = roadNodes[vipc];
                        if (!roadNode.link.Contains(roadNodec))
                        {
                            roadNode.link.Add(roadNodec);
                            roadNodec.link.Add(roadNode);

                            CarcassonneQuarter[] connectedQuarters = GetQuarters(vip, vipc);
                            CarcassonneRoad newRoad = new CarcassonneRoad();
                            newRoad.pointA = vip;
                            newRoad.pointB = vipc;
                            newRoad.quarterA = connectedQuarters[0];
                            newRoad.quarterB = connectedQuarters[1];

                            if (connectedQuarters[0] != null) connectedQuarters[0].roads.Add(newRoad);
                            if (connectedQuarters[1] != null) connectedQuarters[1].roads.Add(newRoad);

                            roads.Add(newRoad);
                        }
                    }
                }

                quarter.Shrink(_rGen.Range(2, 4));
            }

After the quarters have been created, the roads network will be defined. Each quarter it iterated over to extract the road points that would surround it. As we go through, most of the roads will be flanked by two quarters. The roads and quarters are both assigned to each other for later. The final part we shrink the quarter be a random amount, allowing space for the road to exist in.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
Dictionary<Vector2Int, int> pointCount = new Dictionary<Vector2Int, int>();
            for (int r = 0; r < numberOfQuarters; r++)
            {
                CarcassonneQuarter quarter = quarters[r];
                int quarterSize = quarter.points.Count;
                for (int p = 0; p < quarterSize; p++)
                {
                    Vector2Int vip = quarter[p];
                    roadNodes[vip].shrunkPoints.Add(quarter.shrunkPoints[p]);

                    if (!quarter.externalQuarter)
                    {
                        if (!pointCount.ContainsKey(quarter.points[p]))
                            pointCount.Add(quarter.points[p], 1);
                        else
                            pointCount[quarter.points[p]]++;

                        int indexB = p < quarterSize - 1 ? p + 1 : 0;
                        int indexC = p > 0 ? p - 1 : quarterSize - 1;
                        Vector2Int vipb = quarter[indexB];
                        Vector2Int vipc = quarter[indexC];

                        if (!pointMap.ContainsKey(vip))
                            pointMap.Add(vip, new List<Vector2Int>());
                        if (!pointMap[vip].Contains(vipb))
                            pointMap[vip].Add(vipb);
                        if (!pointMap[vip].Contains(vipc))
                            pointMap[vip].Add(vipc);

                        foreach (CarcassonneRoad road in quarter.roads)
                        {
                            road.generate = true;
                        }
                        roadNodes[vip].generate = true;
                    }
                }
            }

Now that the roads have been created and the quarters have been shrunk the real physical points of the road can be assigned. Each road has the shrunk points assigned to them so they can generate. This is only done on roads that are internal to the city.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
            List<GenesisPlot> gplots = new List<GenesisPlot>();
            unbuiltPlots.Clear();
            for (int r = 0; r < numberOfQuarters; r++)
            {
                CarcassonneQuarter quarter = quarters[r];
                if (quarter.externalQuarter) continue;

                Plot plot = new Plot(_rGen.generateSeed, quarter.shrunkPoints);
                plot.minArea = _rGen.OutputRange(30, 50);
                plot.maxArea = _rGen.OutputRange(64, 96);
                plot.minimumAspect = _rGen.OutputRange(0.1f, 0.45f);
                plot.variation = _rGen.OutputRange(0.3f, 0.6f);
                plot.minimumAccessLengthPercent = _rGen.OutputRange(0.01f, 0.05f);
                plot.mergeThreashold = 1.0f;//rGen.OutputRange(0.1f, 0.2f);
                plot.allowTrianglularPlots = true;//rGen.OutputRange(0.1f, 0.2f);
                plot.Split();
                IPlot[] plots = plot.getSubplots;
                int plotCount = plots.Length;
                for (int p = 0; p < plotCount; p++)
                {
                    //Create usable plots from the generated plot data
                    CarcassonnePlot newPlot = new CarcassonnePlot();

                    if (plots[p].area > 550)
                    {
                        Debug.Log(plots[p].notes);
                    }

                    newPlot.AddPlot(plots[p]);
                    quarter.plots.Add(newPlot);
                    GenesisPlot gPlot = newPlot.GenerateGPlot();
                    gPlot.landUse = _rGen.OutputRange(0.6f, 1.0f);
                    gPlot.density = _rGen.OutputRange(2, 7);
                    gPlot.generatePlotMesh = true;
                    gplots.Add(gPlot);
                    unbuiltPlots.Add(gPlot);
                }
            }

The quarters will be too big for a single building so we’ll subdivide them into smaller parcels of land. The Plot class is capable of doing this with specific constraints. The min and max area can allow us to define what size is acceptable for the buildings. The subdivision will continue until the plot is within this size. The minimum aspect will stop plots being generated that are too long and thin. Variation will define how far from the centre a subdivision can be made in normalised space. minimumAccessLengthPercent is defined to make sure each plot has a defined amount of access to the external plot of land. The subdivision will not be allowed to generate plots that have no external access. mergeThreashold defines the ability to merge new plot points with existing plot points. This will stop subdivisions creating plot points that are very close together.
Finally the Split() method is called which will create all the subplots. We then take these subplots and create new GenesisPlots to be used in the creation of buildings.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
settings = GenesisSettings.Get();
            GRandom.seed = seed;
            if (!queueGenerateBuildings)
            {
                int gplotCount = gplots.Count;
                for (int gp = 0; gp < gplotCount; gp++)
                {
                    if (gplots[gp].area > 550)
                        continue;
                    constraints.CheckFacades();
                    BuildingRuntime building = BuildingGenerator.CreateRuntime(gplots[gp], constraints, settings.defaultGeneratedBuildingName);
                    building.Place(gplots[gp].transform.position);
                }
            }

Finally, take the generated plots and create some buildings from it using the defined building constraints file. The buildings are then placed into the position of the plots.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
using UnityEngine;
using System.Collections.Generic;
using BuildR2.Genesis;
using BuildR2.csDelaunay;

namespace BuildR2
{
    /// <summary>
    ///
    /// Project history
    ///
    /// 10th December 2017
    /// Worked on getting more consitant look and feel for the city
    /// Windows now have timber frames thanks to BuildR 2 update
    /// Roads and land parcels now generate basic geometry
    ///  
    /// 18th November 2017
    /// This is an example of large scale city generation using BuildR 2.0
    /// The work is based on Watabou's marvelous Medieval Fantasy City Generator https://watabou.itch.io/medieval-fantasy-city-generator
    /// This is an ongoing project and is currently unfinished but I feel it's a good example of creating buildings at runtime
    /// It is therefore a little buggy and these will be addressed in future releases
    /// Currently we're only generating the buildings. There are no walls, roads or terrain which will follow in a later release of BuildR
    /// If you have any comments, bugs, suggestions or questions don't hesitate to ask me via
    ///
    ///
    ///
    ///     Email: email@jasperstocker.com
    /// or
    ///     Twitter: @jasperstocker
    ///
    /// Thanks
    /// Jasper
    /// </summary>
    ///
    /// Known issues
    /// Some buildings do not generate
    /// Some roofs do not generate
    /// There are issues with how the wall is generating
    ///
    /// TODO
    /// Generate walls
    /// Texture roads
    /// Implement church buildings
    /// Implement keep(?)
    /// Add more facades
    /// Add more window textures
    /// Proper setup mode before generate of geomtry - allow uses to define city gen
    /// Look to started a basic BuildR LOD system - generate LOD levels. Also look into merging whole quarters for distance
    /// Generate interiors
    /// Generate colliders

    public class Carcassonne : MonoBehaviour
    {
        public uint seed = 1;
        public float mapSize = 1000;
        public int voronoiCount = 45;
        public bool queueGenerateBuildings = true;

        //this is a linked point map - each point key is linked to a list of value points as defined within the Voronoi
        public Dictionary<Vector2Int, List<Vector2Int>> pointMap = new Dictionary<Vector2Int, List<Vector2Int>>();
        //Regions are defined areas by the voronoi polygons - each region will contain subplot land parcels for buildings to occupy
        public List<CarcassonneQuarter> quarters = new List<CarcassonneQuarter>();
        public Dictionary<Vector2Int, List<CarcassonneQuarter>> pointQuarters = new Dictionary<Vector2Int, List<CarcassonneQuarter>>();
        //The defined outer perimeter wall of the city
        public CarcassonneWall wall = new CarcassonneWall();
        //The road network points dictionary valued with their Vector2Int position
        public List<CarcassonneRoadNetworkPoint> nodes = new List<CarcassonneRoadNetworkPoint>();
        public Dictionary<Vector2Int, CarcassonneRoadNetworkPoint> roadNodes = new Dictionary<Vector2Int, CarcassonneRoadNetworkPoint>();
        //
        public List<CarcassonneRoad> roads = new List<CarcassonneRoad>();
        //the building contraint file to be used with BuildR 2's runtime generator
        public BuildingContraints constraints;

        private List<GenesisPlot> unbuiltPlots = new List<GenesisPlot>();
        private GenesisSettings settings;

        private VisualPartRuntime _roadMesh;
        private RandomGen _rGen;

        private void Execute()
        {
            //First we'll create a Voroonoi
            _rGen = new RandomGen(seed);
            Rectf map = new Rectf(0, 0, mapSize, mapSize);
            List<Vector2f> points = new List<Vector2f>();
            for (int i = 0; i < voronoiCount; i++)
                points.Add(new Vector2f(_rGen.Range(0, mapSize), _rGen.Range(0, mapSize)));
            Voronoi v = new Voronoi(points, map);
            v.LloydRelaxation(1);

            //With the Voronoi, we will then create the city quarters and city road layout
            List<List<Vector2f>> vQuarters = v.Regions();
            int numberOfQuarters = vQuarters.Count;
            for (int r = 0; r < numberOfQuarters; r++)
            {
                List<Vector2f> vQuarter = vQuarters[r];
                int quarterSize = vQuarter.Count;

                CarcassonneQuarter newQuarter = new CarcassonneQuarter();
                quarters.Add(newQuarter);
                for (int s = 0; s < quarterSize; s++)
                {
                    Vector2Int vip = new Vector2Int(vQuarter[s].x, vQuarter[s].y);
                    newQuarter.points.Add(vip);
                    if (!pointQuarters.ContainsKey(vip))
                        pointQuarters.Add(vip, new List<CarcassonneQuarter>());
                    pointQuarters[vip].Add(newQuarter);
                    if (!newQuarter.externalQuarter && PointOnEdge(vip)) newQuarter.externalQuarter = true;//
                }
                newQuarter.CalculateCenter();
            }
            for (int q = 0; q < numberOfQuarters; q++)
            {
                CarcassonneQuarter quarter = quarters[q];
                int quarterSize = quarter.points.Count;
                for (int s = 0; s < quarterSize; s++)
                {
                    Vector2Int vip = quarter.points[s];
                    int indexB = s < quarterSize - 1 ? s + 1 : 0;
                    int indexC = s > 0 ? s - 1 : quarterSize - 1;
                    Vector2Int vipb = quarter.points[indexB];
                    Vector2Int vipc = quarter.points[indexC];

                    //Link the road points up to create the network
                    CarcassonneRoadNetworkPoint roadNode;
                    if (!roadNodes.ContainsKey(vip))
                    {
                        roadNode = new CarcassonneRoadNetworkPoint();
                        roadNode.position = vip;
                        roadNodes.Add(vip, roadNode);
                        nodes.Add(roadNode);
                    }
                    else
                    {
                        roadNode = roadNodes[vip];
                    }

                    if (roadNodes.ContainsKey(vipb))
                    {
                        CarcassonneRoadNetworkPoint roadNodeb = roadNodes[vipb];
                        if (!roadNode.link.Contains(roadNodeb))
                        {
                            roadNode.link.Add(roadNodeb);
                            roadNodeb.link.Add(roadNode);

                            CarcassonneRoad newRoad = new CarcassonneRoad();
                            CarcassonneQuarter[] connectedQuarters = GetQuarters(vip, vipb);
                            newRoad.pointA = vip;
                            newRoad.pointB = vipb;
                            newRoad.quarterA = connectedQuarters[0];
                            newRoad.quarterB = connectedQuarters[1];

                            if (connectedQuarters[0] != null) connectedQuarters[0].roads.Add(newRoad);
                            if (connectedQuarters[1] != null) connectedQuarters[1].roads.Add(newRoad);

                            roads.Add(newRoad);
                        }
                    }

                    if (roadNodes.ContainsKey(vipc))
                    {
                        CarcassonneRoadNetworkPoint roadNodec = roadNodes[vipc];
                        if (!roadNode.link.Contains(roadNodec))
                        {
                            roadNode.link.Add(roadNodec);
                            roadNodec.link.Add(roadNode);

                            CarcassonneQuarter[] connectedQuarters = GetQuarters(vip, vipc);
                            CarcassonneRoad newRoad = new CarcassonneRoad();
                            newRoad.pointA = vip;
                            newRoad.pointB = vipc;
                            newRoad.quarterA = connectedQuarters[0];
                            newRoad.quarterB = connectedQuarters[1];

                            if (connectedQuarters[0] != null) connectedQuarters[0].roads.Add(newRoad);
                            if (connectedQuarters[1] != null) connectedQuarters[1].roads.Add(newRoad);

                            roads.Add(newRoad);
                        }
                    }
                }

                quarter.Shrink(_rGen.Range(2, 4));
            }

            //Build the road network map
            //make a note of how many connections each point connects to
            Dictionary<Vector2Int, int> pointCount = new Dictionary<Vector2Int, int>();
            for (int r = 0; r < numberOfQuarters; r++)
            {
                CarcassonneQuarter quarter = quarters[r];
                int quarterSize = quarter.points.Count;
                for (int p = 0; p < quarterSize; p++)
                {
                    Vector2Int vip = quarter[p];
                    roadNodes[vip].shrunkPoints.Add(quarter.shrunkPoints[p]);

                    if (!quarter.externalQuarter)
                    {
                        if (!pointCount.ContainsKey(quarter.points[p]))
                            pointCount.Add(quarter.points[p], 1);
                        else
                            pointCount[quarter.points[p]]++;

                        int indexB = p < quarterSize - 1 ? p + 1 : 0;
                        int indexC = p > 0 ? p - 1 : quarterSize - 1;
                        Vector2Int vipb = quarter[indexB];
                        Vector2Int vipc = quarter[indexC];

                        if (!pointMap.ContainsKey(vip))
                            pointMap.Add(vip, new List<Vector2Int>());
                        if (!pointMap[vip].Contains(vipb))
                            pointMap[vip].Add(vipb);
                        if (!pointMap[vip].Contains(vipc))
                            pointMap[vip].Add(vipc);

                        foreach (CarcassonneRoad road in quarter.roads)
                        {
                            road.generate = true;
                        }
                        roadNodes[vip].generate = true;
                    }
                }
            }

            //Find a point in the network that is on the city limits
            //This will be a point that has two connections
            //Internal city points will connect to three points
            //External points will connect to nothing
            Vector2Int startPoint = new Vector2Int();
            foreach (KeyValuePair<Vector2Int, int> var in pointCount)
            {
                if (var.Value == 2)
                {
                    startPoint = var.Key;
                    break;
                }
            }

            //Iterate through the points to build the external city wall point list
            if (pointMap.ContainsKey(startPoint))
            {
                List<Vector2Int> wallPoints = new List<Vector2Int>();
                Vector2Int currentPoint = startPoint;
                wallPoints.Add(currentPoint);
                while (true)
                {
                    List<Vector2Int> connections = pointMap[currentPoint];
                    int connectionCount = connections.Count;
                    for (int c = 0; c < connectionCount; c++)
                    {
                        Vector2Int connection = connections[c];
                        if (pointCount[connection] < 3)
                        {
                            if (!wallPoints.Contains(connection))
                            {
                                wallPoints.Add(connection);
                                break;
                            }
                        }
                    }
                    if (wallPoints.Count > 0 && wallPoints[wallPoints.Count - 1] == currentPoint) break;//no new connection found
                    if (wallPoints.Count > 1 && wallPoints[wallPoints.Count - 1] == wallPoints[0]) break;//wall circle complete
                    currentPoint = wallPoints[wallPoints.Count - 1];
                }

                wall.points.AddRange(wallPoints);
            }

            //Create sub plots from the main quarter shapes
            //Plot class will subdivide based on the defined variables
            List<GenesisPlot> gplots = new List<GenesisPlot>();
            unbuiltPlots.Clear();
            for (int r = 0; r < numberOfQuarters; r++)
            {
                CarcassonneQuarter quarter = quarters[r];
                if (quarter.externalQuarter) continue;

                Plot plot = new Plot(_rGen.generateSeed, quarter.shrunkPoints);
                plot.minArea = _rGen.OutputRange(30, 50);
                plot.maxArea = _rGen.OutputRange(64, 96);
                plot.minimumAspect = _rGen.OutputRange(0.1f, 0.45f);
                plot.variation = _rGen.OutputRange(0.3f, 0.6f);
                plot.minimumAccessLengthPercent = -1.0f;//rGen.OutputRange(0.1f, 0.2f);
                plot.minimumAccessLengthPercent = _rGen.OutputRange(0.01f, 0.05f);
                plot.mergeThreashold = 1.0f;//rGen.OutputRange(0.1f, 0.2f);
                plot.allowTrianglularPlots = true;//rGen.OutputRange(0.1f, 0.2f);
                plot.Split();
                IPlot[] plots = plot.getSubplots;
                int plotCount = plots.Length;
                for (int p = 0; p < plotCount; p++)
                {
                    //Create usable plots from the generated plot data
                    CarcassonnePlot newPlot = new CarcassonnePlot();

                    if (plots[p].area > 550)
                    {
                        Debug.Log(plots[p].notes);
                    }

                    newPlot.AddPlot(plots[p]);
                    quarter.plots.Add(newPlot);
                    GenesisPlot gPlot = newPlot.GenerateGPlot();
                    gPlot.landUse = _rGen.OutputRange(0.6f, 1.0f);
                    gPlot.density = _rGen.OutputRange(2, 7);
                    gPlot.generatePlotMesh = true;
                    gplots.Add(gPlot);
                    unbuiltPlots.Add(gPlot);
                }
            }

            //Generate the buildings from the sub plot shapes and the building contstaints
            settings = GenesisSettings.Get();
            GRandom.seed = seed;
            if (!queueGenerateBuildings)
            {
                int gplotCount = gplots.Count;
                for (int gp = 0; gp < gplotCount; gp++)
                {
                    if (gplots[gp].area > 550)
                        continue;
                    constraints.CheckFacades();
                    BuildingRuntime building = BuildingGenerator.CreateRuntime(gplots[gp], constraints, settings.defaultGeneratedBuildingName);
                    building.Place(gplots[gp].transform.position);
                }
            }

            if (_roadMesh != null)
                _roadMesh.Clear();
            else
                _roadMesh = VisualPartRuntime.GetPoolItem();

            _roadMesh.name = "road mesh";
            _roadMesh.transform.parent = null;
            int roadCount = roads.Count;
            for (int r = 0; r < roadCount; r++)
            {
                if (!roads[r].generate) continue;
                RawMeshData meshData = roads[r].GenerateRoad();
                if (meshData != null)
                    _roadMesh.dynamicMesh.AddData(meshData);
            }
            int roadPointCount = nodes.Count;
            for (int rn = 0; rn < roadPointCount; rn++)
            {
                if (!nodes[rn].generate) continue;
                RawMeshData meshData = nodes[rn].GenerateRoad();
                if (meshData != null)
                    _roadMesh.dynamicMesh.AddData(meshData);
            }
            int quarterCount = quarters.Count;
            for (int q = 0; q < quarterCount; q++)
            {
                CarcassonneQuarter quarter = quarters[q];
                int plotCount = quarter.plots.Count;
                for (int p = 0; p < plotCount; p++)
                {
                    if (quarter.plots[p].data == null) continue;
                    RawMeshData meshData = Plot.GeneratePlot(quarter.plots[p].data);
                    if (meshData != null)
                        _roadMesh.dynamicMesh.AddData(meshData);
                }
            }
            _roadMesh.GenerateFromDynamicMesh();
        }

        private void Start()
        {
            Invoke("ExecuteTimeMonitored", 0.93f);
        }

        private void ExecuteTimeMonitored()
        {
            Execute();
        }

        private void Update()
        {
            //Display(); //Uncomment to see debug scene output

            if (queueGenerateBuildings)
            {
                int gplotCount = unbuiltPlots.Count;

                if (gplotCount > 0)
                {
                    float t = Time.realtimeSinceStartup;
                    while (Time.realtimeSinceStartup - t < 0.030f)
                    {
                        GenesisPlot plot = unbuiltPlots[0];
                        unbuiltPlots.RemoveAt(0);

                        if (plot.area < 700 && plot.numberOfPoints > 3)
                        {
                            constraints.CheckFacades();
                            BuildingRuntime building = BuildingGenerator.CreateRuntime(plot, constraints, settings.defaultGeneratedBuildingName);
                            building.Place(plot.transform.position + Vector3.up * 0.1f);
                        }

                        gplotCount--;
                        if (gplotCount == 0)
                            break;
                    }
                }
            }
        }

        private bool PointOnEdge(Vector2Int point)
        {
            if (point.vx == 0) return true;
            if (point.vx == mapSize) return true;
            if (point.vy == 0) return true;
            if (point.vy == mapSize) return true;
            return false;
        }

        private CarcassonneQuarter[] GetQuarters(Vector2Int pointA, Vector2Int pointB)
        {
            CarcassonneQuarter[] output = new CarcassonneQuarter[2];
            output[0] = null;
            output[1] = null;

            List<CarcassonneQuarter> quartersA = pointQuarters[pointA];
            List<CarcassonneQuarter> quartersB = pointQuarters[pointB];

            int quartersACount = quartersA.Count;
            int quartersBCount = quartersB.Count;

            for (int a = 0; a < quartersACount; a++)
            {
                CarcassonneQuarter qa = quartersA[a];
                for (int b = 0; b < quartersBCount; b++)
                {
                    CarcassonneQuarter qb = quartersB[b];

                    if (qa == qb)
                    {
                        if (output[0] == null)
                            output[0] = qa;
                        else if (output[1] == null)
                            output[1] = qa;
                        break;
                    }
                }
            }

            return output;
        }

        /// <summary>
        /// Display the data of the Voronoi/City at runtime
        /// </summary>
        private void Display()
        {
            int quartersCount = quarters.Count;

            for (int r = 0; r < quartersCount; r++)
            {
                CarcassonneQuarter quarter = quarters[r];
                if (quarter.externalQuarter) continue;
                int quarterSize = quarter.points.Count;

                int plotCount = quarter.plots.Count;
                for (int p = 0; p < plotCount; p++)
                {
                    CarcassonnePlot plot = quarter.plots[p];
                    int plotSize = plot.points.Count;
                    for (int pp = 0; pp < plotSize; pp++)
                    {
                        int indexB = pp < plotSize - 1 ? pp + 1 : 0;
                        Vector3 p0 = plot.points[pp].vector3XZ;
                        Vector3 p1 = plot.points[indexB].vector3XZ;
                        Debug.DrawLine(p0, p1, Color.blue);
                    }
                }

                for (int s = 0; s < quarterSize; s++)
                {
                    Vector3 p0 = quarter.shrunkPoints[s];
                    int indexB = s < quarterSize - 1 ? s + 1 : 0;
                    Vector3 p1 = quarter.shrunkPoints[indexB];

                    p0.z = p0.y;
                    p1.z = p1.y;

                    p0.y = 0;
                    p1.y = 0;

                    Debug.DrawLine(p0, p1, Color.red);
                }
            }

            int wallLength = wall.points.Count;
            for (int w = 0; w < wallLength; w++)
            {
                int indexB = w < wallLength - 1 ? w + 1 : 0;

                Vector3 p0 = wall.points[w].vector3XZ;
                Vector3 p1 = wall.points[indexB].vector3XZ;

                Debug.DrawLine(p0, p1, Color.green);
            }

            foreach (KeyValuePair<Vector2Int, List<CarcassonneQuarter>> var in pointQuarters)
            {
                Color col = new Color(var.Key.vx % 1f, var.Key.vx * var.Key.vy % 1, var.Key.vy % 1f);
                foreach (CarcassonneQuarter q in var.Value)
                {
                    Debug.DrawLine(var.Key.vector3XZ, q.center.vector3XZ, col);
                }
            }
        }
    }
}

Comments are closed.