parallelized multiple parts of the program for increased speed.

This commit is contained in:
Grimsace
2026-02-05 14:21:44 -06:00
parent ba66d00de3
commit 0793ba6ade
+82 -32
View File
@@ -6,6 +6,8 @@ import (
"image/draw" "image/draw"
"math" "math"
"math/rand" "math/rand"
"runtime"
"sync"
"github.com/aquilax/go-perlin" "github.com/aquilax/go-perlin"
"github.com/disintegration/imaging" "github.com/disintegration/imaging"
@@ -26,25 +28,41 @@ func GenerateHeightmap(width, height, octaves int, scale float64, seed int64) im
scale = 100.0 scale = 100.0
} }
for x := range width { numGoroutines := runtime.NumCPU()
for y := range height { var wg sync.WaitGroup
var noise float64 rowsPerGoroutine := height / numGoroutines
frequency := 1.0
amplitude := 1.0
maxAmplitude := 0.0
for range octaves { for i := 0; i < numGoroutines; i++ {
noise += p.Noise2D(float64(x)*frequency/scale, float64(y)*frequency/scale) * amplitude startY := i * rowsPerGoroutine
maxAmplitude += amplitude endY := startY + rowsPerGoroutine
amplitude /= 2.0 if i == numGoroutines-1 {
frequency *= 2.0 endY = height
}
noise /= maxAmplitude
grayColor := uint8((noise + 1) * 127.5)
img.SetGray(x, y, color.Gray{Y: grayColor})
} }
wg.Add(1)
go func(startY, endY int) {
defer wg.Done()
for y := startY; y < endY; y++ {
for x := 0; x < width; x++ {
var noise float64
frequency := 1.0
amplitude := 1.0
maxAmplitude := 0.0
for j := 0; j < octaves; j++ {
noise += p.Noise2D(float64(x)*frequency/scale, float64(y)*frequency/scale) * amplitude
maxAmplitude += amplitude
amplitude /= 2.0
frequency *= 2.0
}
noise /= maxAmplitude
grayColor := uint8((noise + 1) * 127.5)
img.SetGray(x, y, color.Gray{Y: grayColor})
}
}
}(startY, endY)
} }
wg.Wait()
return img return img
} }
@@ -201,29 +219,61 @@ func GenerateTrees(img *image.RGBA, lakePixels, roadPixels []image.Point, minTre
var treePixels []image.Point var treePixels []image.Point
// 5. Draw the trees. // 5. Draw the trees.
for _, p := range allPoints { numGoroutines := runtime.NumCPU()
size := minTreeSize + randSrc.Float64()*(maxTreeSize-minTreeSize) if len(allPoints) < numGoroutines {
if size <= 0 { numGoroutines = len(allPoints)
continue }
if numGoroutines == 0 {
return nil
}
var wg sync.WaitGroup
results := make(chan []image.Point, numGoroutines)
pointsPerGoroutine := (len(allPoints) + numGoroutines - 1) / numGoroutines
for i := 0; i < numGoroutines; i++ {
start := i * pointsPerGoroutine
end := start + pointsPerGoroutine
if end > len(allPoints) {
end = len(allPoints)
} }
r := size / 2
// Use a simple pixel-by-pixel circle drawing method wg.Add(1)
for y := p.Y - int(r); y <= p.Y+int(r); y++ { go func(points []image.Point, seed int64) {
for x := p.X - int(r); x <= p.X+int(r); x++ { defer wg.Done()
pt := image.Point{X: x, Y: y} localRand := rand.New(rand.NewSource(seed))
if !pt.In(img.Bounds()) || isLake[pt] || isRoad[pt] { localTreePixels := make([]image.Point, 0)
for _, p := range points {
size := minTreeSize + localRand.Float64()*(maxTreeSize-minTreeSize)
if size <= 0 {
continue continue
} }
r := size / 2
for y := p.Y - int(r); y <= p.Y+int(r); y++ {
for x := p.X - int(r); x <= p.X+int(r); x++ {
pt := image.Point{X: x, Y: y}
if !pt.In(img.Bounds()) || isLake[pt] || isRoad[pt] {
continue
}
if (math.Pow(float64(x-p.X), 2) + math.Pow(float64(y-p.Y), 2)) <= r*r { if (math.Pow(float64(x-p.X), 2) + math.Pow(float64(y-p.Y), 2)) <= r*r {
// Blend the tree color with the background img.Set(x, y, color.RGBA{R: 0, G: 100, B: 0, A: 255})
// For simplicity, we just set a solid color for now. localTreePixels = append(localTreePixels, pt)
img.Set(x, y, color.RGBA{R: 0, G: 100, B: 0, A: 255}) }
treePixels = append(treePixels, pt) }
} }
} }
} results <- localTreePixels
}(allPoints[start:end], seed+int64(i))
} }
wg.Wait()
close(results)
for res := range results {
treePixels = append(treePixels, res...)
}
return treePixels return treePixels
} }