This is a road network generation tool based on Müller et al.. The basic idea is to extend L-Systems to support intersection and snapping (self-sensitivity) and be guided by image maps (population density).
It features:
- road network generation/manipulation
- basic road network geometry construction
- basic building allotment distribution
- Add RoadNetwork to a game object in your scene an set generation parameters (e.g., street segment length)
- Add MockTerrain (or your own ITerrain implementation)
- [Optional] Add DummyAllotmentBuilder and RoadDensityMap. RoadDensityMap requires that you reference RoadNetwork.
- Add RoadDensityBasedSettlementSpawner (or your own settlement spawner implementation) and reference DummyAllotmentBuilder (or your own IAllotmentBuilder implementation) and RoadDensityMap
- Add RoadNetworkMesh and set materials (road segments and crossing), mesh detail (e.g., length step) and reference to RoadNetwork and terrain
-
Quadtree Params: The min. coordinates and size of the quatree (in world units) in which the road segments are put to optimize collision detections.
-
Quadtree Max Objects/Levels: Some other control parameters for the quadtree (max. number of items per quadrant and max. number of quadtree levels).
-
Segment Count Limit: Each road segment expands forward or spawns child road segments (branch) at each derivation step. This parameter controls the maximum amount of expansions a road segment can have.
-
Derivation Step Limit: Each derivation step is an opportunity for an existing road segments to expand or branch. This parameter controls the number of derivation steps.
-
Street/Highway Segment Length/Width: Road segments can be streets or highways. This parameter controls their width/length (in world units).
-
Street/Highway Branch Probability: This parameter is the normalized (0-1) probability of a road segment spawning another road segment.
-
Street/Highway Branch Population: TODO
-
Street Branch Time Delay: Number of derivation steps until the probability for streets to branch is evaluated.
-
Minimum Intersection = TODO
-
Snap Distance: Road segments "snap" to each other in order to form network-like structures. This is the maximum distance (in world units) that a road segment can have from another until it's snapped.
-
Allotment Min/Max Half Diagonal: Building allotments are placed alongside road segments at a certain derivation interval. This is the min./max. diagonal size (in world units) of an allotment.
-
Allotment Min/Max Aspect: Once the diagonal size is sorted, the aspect length is sorted too. With the diagonal and aspect, we can define width and height. This controls the aspect (width / height) of the allotments.
-
Allotment Placement Loop: TODO
-
Settlement Spawn Delay: A settlement is a region where building allotments are placed. This parameter controls how many derivation steps needs to pass until a settlement can be randomly sorted.
-
Settlement Radius: This parameter controls the size of settlement from the point they started (in world units).
-
Settlement Crossing/Highway Probabilities: This parameter is the normalized (0-1) probability of a road segment that is either a highway or is in a crossing (i.e., has 2 orthogonal road segments connected to it) spawning a settlement.
-
Generate Highways/Streets: Enable/disable the generation of highways/streets.
Add GlobalSeeder to a game object in your scene to control pseudo-random number generation.
using System.Collections.Generic;
using RoadGen;
(...)
List<Segment> segments;
Quadtree quadtree;
RoadNetworkGenerator.DebugData debugData;
RoadNetworkGenerator.Generate(out segments, out quadtree, out debugData);
using System.Collections.Generic;
using RoadGen;
(...)
HashSet<Segment> visited = new HashSet<Segment>();
foreach (var segment in segments)
{
RoadNetworkTraversal.PreOrder(segment, (a) =>
{
// my visitation logic
return true;
},
mask,
ref visited);
}
using System.Collections.Generic;
using RoadGen;
(...)
struct MyContext
{
// my data
}
(...)
bool MyVisitor(Segment segment, ref MyContext myContext)
{
// my visitation logic
return true;
}
(...)
HashSet<Segment> visited = new HashSet<Segment>();
MyContext myContext;
foreach (var segment in roadNetwork.Segments)
{
RoadNetworkTraversal.PreOrder(segment,
ref myContext,
MyVisitor,
RoadNetworkTraversal.HIGHWAYS_MASK | RoadNetworkTraversal.STREETS_MASK,
ref visited);
}
using System.Collections.Generic;
using RoadGen;
(...)
struct MyContext
{
// my context data
}
struct MyUserData
{
// my user data
}
(...)
bool MyVisitor(Segment segment, ref MyContext myContext, MyUserData i_myUserData, out MyUserData o_myUserData)
{
o_myUserData = new MyUserData();
// my visitation logic
return true;
}
(...)
HashSet<Segment> visited = new HashSet<Segment>();
MyContext myContext;
MyUserData myUserData;
foreach (var segment in roadNetwork.Segments)
{
RoadNetworkTraversal.PreOrder(segment,
ref myContext,
myUserData,
MyVisitor,
RoadNetworkTraversal.HIGHWAYS_MASK | RoadNetworkTraversal.STREETS_MASK,
ref visited);
}
using UnityEngine;
using System.Collections.Generic;
using System.Linq;
using RoadGen;
(...)
var geometry = RoadNetworkGeometryBuilder.Build(
scale,
Config.highwaySegmentWidth,
Config.streetSegmentWidth,
lengthStep,
segments,
RoadNetworkTraversal.HIGHWAYS_MASK | RoadNetworkTraversal.STREETS_MASK
);
List<Vector3> vertices = new List<Vector3>();
geometry.GetSegmentPositions().ForEach((p) =>
{
vertices.Add(new Vector3(p.x, heightmap.GetHeight(p.x, p.y), p.y));
});
geometry.GetCrossingPositions().ForEach((p) =>
{
vertices.Add(new Vector3(p.x, heightmap.GetHeight(p.x, p.y), p.y));
});
Mesh mesh = new Mesh();
mesh.vertices = vertices.ToArray();
mesh.triangles = geometry.GetCrossingIndices().ToArray();
mesh.uv = geometry.GetCrossingUvs().ToArray();
mesh.RecalculateNormals();
Extend RoadGen.IHeightmap and implement:
float GetHeight(float x, float y);
bool Finished();
Extend RoadGen.IAllotmentBuilder and implement:
GameObject Build(Allotment allotment, IHeightmap heightmap);