added basic river generation
This commit is contained in:
@@ -101,9 +101,11 @@ func main() {
|
|||||||
// Initial image generation
|
// Initial image generation
|
||||||
noiseImg := GenerateHeightmap(settings.Width, settings.Height, int(settings.Detail), 100.0, settings.Seed)
|
noiseImg := GenerateHeightmap(settings.Width, settings.Height, int(settings.Detail), 100.0, settings.Seed)
|
||||||
lakeImage, lakePixels := GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, noiseImg, settings.Seed)
|
lakeImage, lakePixels := GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, noiseImg, settings.Seed)
|
||||||
finalImage := lakeImage.(*image.RGBA)
|
riverImage, riverPixels := GenerateRivers(settings.Width, settings.Height, settings.Rivers, settings.MinRiverWidth, settings.MaxRiverWidth, settings.RiverCurvyness, lakeImage, lakePixels, settings.Seed)
|
||||||
GenerateTrees(finalImage, lakePixels, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, settings.Seed)
|
finalImage := riverImage.(*image.RGBA)
|
||||||
darkenedHeightmap := DarkenLakeAreas(noiseImg, lakePixels)
|
allWaterPixels := append(lakePixels, riverPixels...)
|
||||||
|
GenerateTrees(finalImage, allWaterPixels, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, settings.Seed)
|
||||||
|
darkenedHeightmap := DarkenLakeAreas(noiseImg, allWaterPixels)
|
||||||
compositeImg := ApplyRoughness(darkenedHeightmap, settings.Roughness)
|
compositeImg := ApplyRoughness(darkenedHeightmap, settings.Roughness)
|
||||||
heightmapImg.Image = compositeImg
|
heightmapImg.Image = compositeImg
|
||||||
canvasImg.Image = finalImage
|
canvasImg.Image = finalImage
|
||||||
@@ -158,6 +160,47 @@ func main() {
|
|||||||
}
|
}
|
||||||
lakeSizeUpperSlider.SetValue(settings.LakeSizeUpper)
|
lakeSizeUpperSlider.SetValue(settings.LakeSizeUpper)
|
||||||
|
|
||||||
|
riversLabel := widget.NewLabel(fmt.Sprintf("Rivers: %d", settings.Rivers))
|
||||||
|
riversSlider := widget.NewSlider(0, 5)
|
||||||
|
riversSlider.OnChanged = func(val float64) {
|
||||||
|
settings.Rivers = int(val)
|
||||||
|
riversLabel.SetText(fmt.Sprintf("Rivers: %d", settings.Rivers))
|
||||||
|
}
|
||||||
|
riversSlider.SetValue(float64(settings.Rivers))
|
||||||
|
|
||||||
|
minRiverWidthLabel := widget.NewLabel(fmt.Sprintf("Min River Width: %.0f%%", settings.MinRiverWidth))
|
||||||
|
minRiverWidthSlider := widget.NewSlider(1, 100)
|
||||||
|
maxRiverWidthLabel := widget.NewLabel(fmt.Sprintf("Max River Width: %.0f%%", settings.MaxRiverWidth))
|
||||||
|
maxRiverWidthSlider := widget.NewSlider(1, 100)
|
||||||
|
|
||||||
|
minRiverWidthSlider.OnChanged = func(val float64) {
|
||||||
|
settings.MinRiverWidth = val
|
||||||
|
if settings.MinRiverWidth > settings.MaxRiverWidth {
|
||||||
|
settings.MaxRiverWidth = settings.MinRiverWidth
|
||||||
|
maxRiverWidthSlider.SetValue(settings.MaxRiverWidth)
|
||||||
|
}
|
||||||
|
minRiverWidthLabel.SetText(fmt.Sprintf("Min River Width: %.0f%%", settings.MinRiverWidth))
|
||||||
|
}
|
||||||
|
minRiverWidthSlider.SetValue(settings.MinRiverWidth)
|
||||||
|
|
||||||
|
maxRiverWidthSlider.OnChanged = func(val float64) {
|
||||||
|
settings.MaxRiverWidth = val
|
||||||
|
if settings.MaxRiverWidth < settings.MinRiverWidth {
|
||||||
|
settings.MinRiverWidth = settings.MaxRiverWidth
|
||||||
|
minRiverWidthSlider.SetValue(settings.MinRiverWidth)
|
||||||
|
}
|
||||||
|
maxRiverWidthLabel.SetText(fmt.Sprintf("Max River Width: %.0f%%", settings.MaxRiverWidth))
|
||||||
|
}
|
||||||
|
maxRiverWidthSlider.SetValue(settings.MaxRiverWidth)
|
||||||
|
|
||||||
|
riverCurvynessLabel := widget.NewLabel(fmt.Sprintf("River Curvyness: %.0f%%", settings.RiverCurvyness))
|
||||||
|
riverCurvynessSlider := widget.NewSlider(0, 100)
|
||||||
|
riverCurvynessSlider.OnChanged = func(val float64) {
|
||||||
|
settings.RiverCurvyness = val
|
||||||
|
riverCurvynessLabel.SetText(fmt.Sprintf("River Curvyness: %.0f%%", settings.RiverCurvyness))
|
||||||
|
}
|
||||||
|
riverCurvynessSlider.SetValue(settings.RiverCurvyness)
|
||||||
|
|
||||||
minTreeSizeLabel := widget.NewLabel(fmt.Sprintf("Min Tree Size: %.0fpx", settings.MinTreeSize))
|
minTreeSizeLabel := widget.NewLabel(fmt.Sprintf("Min Tree Size: %.0fpx", settings.MinTreeSize))
|
||||||
minTreeSizeSlider := widget.NewSlider(1, 150)
|
minTreeSizeSlider := widget.NewSlider(1, 150)
|
||||||
maxTreeSizeLabel := widget.NewLabel(fmt.Sprintf("Max Tree Size: %.0fpx", settings.MaxTreeSize))
|
maxTreeSizeLabel := widget.NewLabel(fmt.Sprintf("Max Tree Size: %.0fpx", settings.MaxTreeSize))
|
||||||
@@ -211,7 +254,7 @@ func main() {
|
|||||||
generateBtn.Disable()
|
generateBtn.Disable()
|
||||||
defer generateBtn.Enable()
|
defer generateBtn.Enable()
|
||||||
|
|
||||||
steps := 6
|
steps := 7
|
||||||
currentStep := 0
|
currentStep := 0
|
||||||
|
|
||||||
// Step 1: Generating Heightmap
|
// Step 1: Generating Heightmap
|
||||||
@@ -225,27 +268,35 @@ func main() {
|
|||||||
progressBar.SetText(fmt.Sprintf("Step %d/%d: Generating Lakes", currentStep, steps))
|
progressBar.SetText(fmt.Sprintf("Step %d/%d: Generating Lakes", currentStep, steps))
|
||||||
progressBar.SetValue(float64(currentStep) / float64(steps))
|
progressBar.SetValue(float64(currentStep) / float64(steps))
|
||||||
lakeImage, lakePixels := GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, noiseImg, settings.Seed)
|
lakeImage, lakePixels := GenerateLakes(settings.Width, settings.Height, settings.Lakes, settings.LakeSizeLower, settings.LakeSizeUpper, noiseImg, settings.Seed)
|
||||||
finalImage := lakeImage.(*image.RGBA)
|
|
||||||
|
|
||||||
// Step 3: Darkening Lake Areas
|
// Step 3: Generating Rivers
|
||||||
currentStep++
|
currentStep++
|
||||||
progressBar.SetText(fmt.Sprintf("Step %d/%d: Darkening Lake Areas", currentStep, steps))
|
progressBar.SetText(fmt.Sprintf("Step %d/%d: Generating Rivers", currentStep, steps))
|
||||||
progressBar.SetValue(float64(currentStep) / float64(steps))
|
progressBar.SetValue(float64(currentStep) / float64(steps))
|
||||||
darkenedHeightmap := DarkenLakeAreas(noiseImg, lakePixels)
|
riverImage, riverPixels := GenerateRivers(settings.Width, settings.Height, settings.Rivers, settings.MinRiverWidth, settings.MaxRiverWidth, settings.RiverCurvyness, lakeImage, lakePixels, settings.Seed)
|
||||||
|
|
||||||
// Step 4: Applying Roughness
|
finalImage := riverImage.(*image.RGBA)
|
||||||
|
allWaterPixels := append(lakePixels, riverPixels...)
|
||||||
|
|
||||||
|
// Step 4: Darkening Water Areas
|
||||||
|
currentStep++
|
||||||
|
progressBar.SetText(fmt.Sprintf("Step %d/%d: Darkening Water Areas", currentStep, steps))
|
||||||
|
progressBar.SetValue(float64(currentStep) / float64(steps))
|
||||||
|
darkenedHeightmap := DarkenLakeAreas(noiseImg, allWaterPixels)
|
||||||
|
|
||||||
|
// Step 5: Applying Roughness
|
||||||
currentStep++
|
currentStep++
|
||||||
progressBar.SetText(fmt.Sprintf("Step %d/%d: Applying Roughness", currentStep, steps))
|
progressBar.SetText(fmt.Sprintf("Step %d/%d: Applying Roughness", currentStep, steps))
|
||||||
progressBar.SetValue(float64(currentStep) / float64(steps))
|
progressBar.SetValue(float64(currentStep) / float64(steps))
|
||||||
compositeImg := ApplyRoughness(darkenedHeightmap, settings.Roughness)
|
compositeImg := ApplyRoughness(darkenedHeightmap, settings.Roughness)
|
||||||
|
|
||||||
// Step 5: Generating Trees
|
// Step 6: Generating Trees
|
||||||
currentStep++
|
currentStep++
|
||||||
progressBar.SetText(fmt.Sprintf("Step %d/%d: Generating Trees", currentStep, steps))
|
progressBar.SetText(fmt.Sprintf("Step %d/%d: Generating Trees", currentStep, steps))
|
||||||
progressBar.SetValue(float64(currentStep) / float64(steps))
|
progressBar.SetValue(float64(currentStep) / float64(steps))
|
||||||
GenerateTrees(finalImage, lakePixels, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, settings.Seed)
|
GenerateTrees(finalImage, allWaterPixels, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, settings.Seed)
|
||||||
|
|
||||||
// Step 6: Finalizing Images
|
// Step 7: Finalizing Images
|
||||||
currentStep++
|
currentStep++
|
||||||
progressBar.SetText(fmt.Sprintf("Step %d/%d: Finalizing Images", currentStep, steps))
|
progressBar.SetText(fmt.Sprintf("Step %d/%d: Finalizing Images", currentStep, steps))
|
||||||
progressBar.SetValue(float64(currentStep) / float64(steps))
|
progressBar.SetValue(float64(currentStep) / float64(steps))
|
||||||
@@ -318,6 +369,20 @@ func main() {
|
|||||||
lakeSizeLowerSlider,
|
lakeSizeLowerSlider,
|
||||||
lakeSizeUpperLabel,
|
lakeSizeUpperLabel,
|
||||||
lakeSizeUpperSlider,
|
lakeSizeUpperSlider,
|
||||||
|
|
||||||
|
widget.NewLabel(""), // Spacer
|
||||||
|
|
||||||
|
riversLabel,
|
||||||
|
riversSlider,
|
||||||
|
minRiverWidthLabel,
|
||||||
|
minRiverWidthSlider,
|
||||||
|
maxRiverWidthLabel,
|
||||||
|
maxRiverWidthSlider,
|
||||||
|
riverCurvynessLabel,
|
||||||
|
riverCurvynessSlider,
|
||||||
|
|
||||||
|
widget.NewLabel(""), // Spacer
|
||||||
|
|
||||||
minTreeSizeLabel,
|
minTreeSizeLabel,
|
||||||
minTreeSizeSlider,
|
minTreeSizeSlider,
|
||||||
maxTreeSizeLabel,
|
maxTreeSizeLabel,
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ type Settings struct {
|
|||||||
TreeCoverage float64 `json:"tree_coverage"`
|
TreeCoverage float64 `json:"tree_coverage"`
|
||||||
TreeClumpiness float64 `json:"tree_clumpiness"`
|
TreeClumpiness float64 `json:"tree_clumpiness"`
|
||||||
Seed int64 `json:"seed"`
|
Seed int64 `json:"seed"`
|
||||||
|
Rivers int `json:"rivers"`
|
||||||
|
MinRiverWidth float64 `json:"min_river_width"`
|
||||||
|
MaxRiverWidth float64 `json:"max_river_width"`
|
||||||
|
RiverCurvyness float64 `json:"river_curvyness"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Settings) Save() error {
|
func (s *Settings) Save() error {
|
||||||
@@ -67,6 +71,10 @@ func LoadSettings() (*Settings, error) {
|
|||||||
TreeCoverage: 20,
|
TreeCoverage: 20,
|
||||||
TreeClumpiness: 50,
|
TreeClumpiness: 50,
|
||||||
Seed: time.Now().UnixNano(),
|
Seed: time.Now().UnixNano(),
|
||||||
|
Rivers: 0,
|
||||||
|
MinRiverWidth: 1,
|
||||||
|
MaxRiverWidth: 5,
|
||||||
|
RiverCurvyness: 50,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
+247
@@ -7,6 +7,7 @@ import (
|
|||||||
"image/draw"
|
"image/draw"
|
||||||
"math"
|
"math"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/disintegration/imaging"
|
"github.com/disintegration/imaging"
|
||||||
"github.com/ojrac/opensimplex-go"
|
"github.com/ojrac/opensimplex-go"
|
||||||
@@ -174,6 +175,252 @@ func GenerateLakes(width, height, numLakes int, lakeSizeLower, lakeSizeUpper flo
|
|||||||
return canvas, allLakePixels
|
return canvas, allLakePixels
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type River struct {
|
||||||
|
Width float64
|
||||||
|
Start, End image.Point
|
||||||
|
Points []image.Point
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRivers(width, height, numRivers int, minWidth, maxWidth, curvyness float64, inputImage image.Image, lakePixels []image.Point, seed int64) (image.Image, []image.Point) {
|
||||||
|
if numRivers == 0 {
|
||||||
|
return inputImage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas, ok := inputImage.(*image.RGBA)
|
||||||
|
if !ok {
|
||||||
|
canvas = image.NewRGBA(inputImage.Bounds())
|
||||||
|
draw.Draw(canvas, canvas.Bounds(), inputImage, image.Point{}, draw.Src)
|
||||||
|
}
|
||||||
|
|
||||||
|
var allRiverPixels []image.Point
|
||||||
|
randSrc := rand.New(rand.NewSource(seed))
|
||||||
|
avgDim := float64(width+height) / 2.0
|
||||||
|
|
||||||
|
isWater := make(map[image.Point]bool)
|
||||||
|
for _, p := range lakePixels {
|
||||||
|
isWater[p] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
rivers := make([]River, numRivers)
|
||||||
|
for i := 0; i < numRivers; i++ {
|
||||||
|
widthPercent := float64(i) / float64(numRivers-1)
|
||||||
|
if numRivers == 1 {
|
||||||
|
widthPercent = 0.5
|
||||||
|
}
|
||||||
|
rivers[i].Width = maxWidth - widthPercent*(maxWidth-minWidth)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(rivers, func(i, j int) bool {
|
||||||
|
return rivers[i].Width > rivers[j].Width
|
||||||
|
})
|
||||||
|
|
||||||
|
numControlPoints := int(avgDim * 0.03)
|
||||||
|
if numControlPoints < 60 {
|
||||||
|
numControlPoints = 60
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range rivers {
|
||||||
|
r := &rivers[i]
|
||||||
|
|
||||||
|
startEdge := randSrc.Intn(4)
|
||||||
|
endEdge := (startEdge + randSrc.Intn(3) + 1) % 4
|
||||||
|
|
||||||
|
r.Start = getPointOnEdge(width, height, startEdge, randSrc)
|
||||||
|
r.End = getPointOnEdge(width, height, endEdge, randSrc)
|
||||||
|
|
||||||
|
path := calculatePath(r.Start, r.End, curvyness/100.0, avgDim, randSrc, numControlPoints)
|
||||||
|
|
||||||
|
for _, p := range path {
|
||||||
|
if isWater[p] {
|
||||||
|
r.End = p
|
||||||
|
path = calculatePath(r.Start, r.End, curvyness/100.0, avgDim, randSrc, numControlPoints)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
riverWidthPx := (r.Width / 100.0) * avgDim
|
||||||
|
radius := riverWidthPx / 2.0
|
||||||
|
|
||||||
|
for _, p := range path {
|
||||||
|
drawCircle(canvas, p, radius, color.RGBA{R: 0, G: 0, B: 255, A: 255}, &allRiverPixels, isWater)
|
||||||
|
}
|
||||||
|
r.Points = path
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvas, allRiverPixels
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPointOnEdge(width, height, edge int, randSrc *rand.Rand) image.Point {
|
||||||
|
switch edge {
|
||||||
|
case 0: // Top
|
||||||
|
return image.Point{X: randSrc.Intn(width), Y: 0}
|
||||||
|
case 1: // Right
|
||||||
|
return image.Point{X: width - 1, Y: randSrc.Intn(height)}
|
||||||
|
case 2: // Bottom
|
||||||
|
return image.Point{X: randSrc.Intn(width), Y: height - 1}
|
||||||
|
default: // Left
|
||||||
|
return image.Point{X: 0, Y: randSrc.Intn(height)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func calculatePath(start, end image.Point, curvyness, avgDim float64, randSrc *rand.Rand, numControlPoints int) []image.Point {
|
||||||
|
|
||||||
|
dx := end.X - start.X
|
||||||
|
|
||||||
|
dy := end.Y - start.Y
|
||||||
|
|
||||||
|
dist := math.Sqrt(float64(dx*dx + dy*dy))
|
||||||
|
|
||||||
|
if dist == 0 {
|
||||||
|
|
||||||
|
return []image.Point{start}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if curvyness == 0 {
|
||||||
|
|
||||||
|
return bresenham([]image.Point{start, end})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type wave struct {
|
||||||
|
amplitude float64
|
||||||
|
|
||||||
|
numWaves float64
|
||||||
|
|
||||||
|
phase float64
|
||||||
|
}
|
||||||
|
|
||||||
|
waves := make([]wave, 3)
|
||||||
|
|
||||||
|
amp := (avgDim / 10.0) * curvyness
|
||||||
|
|
||||||
|
mainWavelength := avgDim / 4.0
|
||||||
|
|
||||||
|
if mainWavelength < 1 {
|
||||||
|
|
||||||
|
mainWavelength = 1
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
baseNumWaves := (dist / mainWavelength) * curvyness
|
||||||
|
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
|
||||||
|
freqMultiplier := 1.0 + float64(i)
|
||||||
|
|
||||||
|
randomizedNumWaves := baseNumWaves * freqMultiplier * (0.75 + randSrc.Float64()*0.5)
|
||||||
|
|
||||||
|
waves[i] = wave{
|
||||||
|
|
||||||
|
amplitude: amp,
|
||||||
|
|
||||||
|
numWaves: randomizedNumWaves,
|
||||||
|
|
||||||
|
phase: randSrc.Float64() * 2 * math.Pi,
|
||||||
|
}
|
||||||
|
|
||||||
|
amp /= 3
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
controlPoints := make([]image.Point, numControlPoints+1)
|
||||||
|
|
||||||
|
for i := 0; i <= numControlPoints; i++ {
|
||||||
|
|
||||||
|
t := float64(i) / float64(numControlPoints)
|
||||||
|
|
||||||
|
x := float64(start.X) + t*float64(dx)
|
||||||
|
|
||||||
|
y := float64(start.Y) + t*float64(dy)
|
||||||
|
|
||||||
|
perpX, perpY := -float64(dy)/dist, float64(dx)/dist
|
||||||
|
|
||||||
|
totalOffset := 0.0
|
||||||
|
|
||||||
|
for _, w := range waves {
|
||||||
|
|
||||||
|
totalOffset += math.Sin(t*w.numWaves*2*math.Pi+w.phase) * w.amplitude
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply an envelope to ensure start/end points are anchored
|
||||||
|
|
||||||
|
totalOffset *= math.Sin(t * math.Pi)
|
||||||
|
|
||||||
|
x += totalOffset * perpX
|
||||||
|
|
||||||
|
y += totalOffset * perpY
|
||||||
|
|
||||||
|
controlPoints[i] = image.Point{X: int(math.Round(x)), Y: int(math.Round(y))}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return bresenham(controlPoints)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func bresenham(path []image.Point) []image.Point {
|
||||||
|
if len(path) < 2 {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
var fullPath []image.Point
|
||||||
|
for i := 0; i < len(path)-1; i++ {
|
||||||
|
p1, p2 := path[i], path[i+1]
|
||||||
|
dx, dy := p2.X-p1.X, p2.Y-p1.Y
|
||||||
|
absDx, absDy := int(math.Abs(float64(dx))), int(math.Abs(float64(dy)))
|
||||||
|
sx, sy := 1, 1
|
||||||
|
if dx < 0 {
|
||||||
|
sx = -1
|
||||||
|
}
|
||||||
|
if dy < 0 {
|
||||||
|
sy = -1
|
||||||
|
}
|
||||||
|
err := absDx - absDy
|
||||||
|
|
||||||
|
x, y := p1.X, p1.Y
|
||||||
|
for {
|
||||||
|
fullPath = append(fullPath, image.Point{X: x, Y: y})
|
||||||
|
if x == p2.X && y == p2.Y {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
e2 := 2 * err
|
||||||
|
if e2 > -absDy {
|
||||||
|
err -= absDy
|
||||||
|
x += sx
|
||||||
|
}
|
||||||
|
if e2 < absDx {
|
||||||
|
err += absDx
|
||||||
|
y += sy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fullPath
|
||||||
|
}
|
||||||
|
|
||||||
|
func drawCircle(img *image.RGBA, center image.Point, radius float64, c color.Color, pixels *[]image.Point, isWater map[image.Point]bool) {
|
||||||
|
bounds := img.Bounds()
|
||||||
|
r2 := radius * radius
|
||||||
|
for y := int(math.Floor(float64(center.Y) - radius)); y <= int(math.Ceil(float64(center.Y)+radius)); y++ {
|
||||||
|
for x := int(math.Floor(float64(center.X) - radius)); x <= int(math.Ceil(float64(center.X)+radius)); x++ {
|
||||||
|
p := image.Point{X: x, Y: y}
|
||||||
|
if !p.In(bounds) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dx, dy := float64(x-center.X), float64(y-center.Y)
|
||||||
|
if dx*dx+dy*dy <= r2 {
|
||||||
|
if !isWater[p] {
|
||||||
|
img.Set(x, y, c)
|
||||||
|
*pixels = append(*pixels, p)
|
||||||
|
isWater[p] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// DarkenLakeAreas applies a visual darkening effect to the heightmap where lakes exist.
|
// DarkenLakeAreas applies a visual darkening effect to the heightmap where lakes exist.
|
||||||
func DarkenLakeAreas(heightmap image.Image, lakePixels []image.Point) image.Image {
|
func DarkenLakeAreas(heightmap image.Image, lakePixels []image.Point) image.Image {
|
||||||
bounds := heightmap.Bounds()
|
bounds := heightmap.Bounds()
|
||||||
|
|||||||
Reference in New Issue
Block a user