reworked road generation with focus on main roads, secondary streets and smaller arterial roads

This commit is contained in:
grimsace
2026-04-02 14:48:34 -05:00
parent 0030d86719
commit a034a2c5db
2 changed files with 384 additions and 104 deletions
+53 -4
View File
@@ -173,22 +173,68 @@ func GenerateBuildings(
searchTries := 100 // Number of attempts to find a spot for a building around an anchor
maxPlacementAttempts := settings.NumBuildings * 5 // To prevent infinite loops
minBuildingSizePx, maxBuildingSizePx := getBuildingSizeRangePixels(settings, width, height)
anchorUsage := make(map[image.Point]int, len(anchorPoints))
normalAnchorCap := 0
exitAnchorCap := 0
if len(normalRoadAnchors) > 0 {
normalAnchorCap = max(2, int(math.Ceil((float64(settings.NumBuildings)/float64(len(normalRoadAnchors)))*1.15)))
}
if len(exitRoadAnchors) > 0 {
exitAnchorCap = max(1, int(math.Ceil((float64(settings.NumBuildings)/float64(len(exitRoadAnchors)))*0.20)))
}
pickAnchorWithCapacity := func(candidates []image.Point, capLimit int) (image.Point, bool) {
if len(candidates) == 0 {
return image.Point{}, false
}
if capLimit <= 0 {
return candidates[randSrc.Intn(len(candidates))], true
}
best := candidates[randSrc.Intn(len(candidates))]
bestCount := anchorUsage[best]
for tries := 0; tries < min(16, len(candidates)*2); tries++ {
candidate := candidates[randSrc.Intn(len(candidates))]
count := anchorUsage[candidate]
if count < capLimit {
return candidate, true
}
if count < bestCount {
best = candidate
bestCount = count
}
}
if bestCount < capLimit {
return best, true
}
return image.Point{}, false
}
for buildingsPlaced < settings.NumBuildings && maxPlacementAttempts > 0 {
maxPlacementAttempts--
// Select an anchor point for the new building
var anchor image.Point
usedRoadAnchor := false
if randSrc.Float64() > settings.BuildingDistribution/100.0 {
// Buildings should only rarely use exit-road anchors.
useExitAnchor := len(exitRoadAnchors) > 0 && randSrc.Float64() < 0.02
if useExitAnchor {
anchor = exitRoadAnchors[randSrc.Intn(len(exitRoadAnchors))]
if a, ok := pickAnchorWithCapacity(exitRoadAnchors, exitAnchorCap); ok {
anchor = a
usedRoadAnchor = true
}
} else if len(normalRoadAnchors) > 0 {
anchor = normalRoadAnchors[randSrc.Intn(len(normalRoadAnchors))]
if a, ok := pickAnchorWithCapacity(normalRoadAnchors, normalAnchorCap); ok {
anchor = a
usedRoadAnchor = true
}
} else if len(anchorPoints) > 0 {
anchor = anchorPoints[randSrc.Intn(len(anchorPoints))]
} else {
if a, ok := pickAnchorWithCapacity(anchorPoints, normalAnchorCap); ok {
anchor = a
usedRoadAnchor = true
}
}
if !usedRoadAnchor {
p, ok := sampleRandomLandPoint(width, height, waterMask, roadMask, randSrc)
if !ok {
continue
@@ -247,6 +293,9 @@ func GenerateBuildings(
img.Set(p.X, p.Y, buildingColor)
buildingMask.SetPoint(p)
}
if usedRoadAnchor {
anchorUsage[anchor]++
}
buildings = append(buildings, pixels)
buildingsPlaced++
break // Move to the next building