parallelized multiple parts of the program for increased speed.
This commit is contained in:
+82
-32
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user