added bump map
This commit is contained in:
+57
@@ -0,0 +1,57 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"math"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateBumpMap creates a bump map from a heightmap.
|
||||||
|
func GenerateBumpMap(heightmap *image.RGBA, width, height int, depth float64) *image.RGBA {
|
||||||
|
bumpMap := image.NewRGBA(image.Rect(0, 0, width, height))
|
||||||
|
|
||||||
|
sobelX := [][]int{
|
||||||
|
{-1, 0, 1},
|
||||||
|
{-2, 0, 2},
|
||||||
|
{-1, 0, 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
sobelY := [][]int{
|
||||||
|
{-1, -2, -1},
|
||||||
|
{0, 0, 0},
|
||||||
|
{1, 2, 1},
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := 1; y < height-1; y++ {
|
||||||
|
for x := 1; x < width-1; x++ {
|
||||||
|
var gx, gy float64
|
||||||
|
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
for j := 0; j < 3; j++ {
|
||||||
|
gray, _, _, _ := heightmap.At(x+i-1, y+j-1).RGBA()
|
||||||
|
intensity := float64(gray >> 8)
|
||||||
|
gx += intensity * float64(sobelX[j][i])
|
||||||
|
gy += intensity * float64(sobelY[j][i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nx := gx
|
||||||
|
ny := gy
|
||||||
|
nz := 1.0 / depth
|
||||||
|
|
||||||
|
length := math.Sqrt(nx*nx + ny*ny + nz*nz)
|
||||||
|
if length > 0 {
|
||||||
|
nx /= length
|
||||||
|
ny /= length
|
||||||
|
nz /= length
|
||||||
|
}
|
||||||
|
|
||||||
|
r := uint8((nx + 1.0) * 127.5)
|
||||||
|
g := uint8((ny + 1.0) * 127.5)
|
||||||
|
b := uint8(nz * 255.0)
|
||||||
|
|
||||||
|
bumpMap.Set(x, y, color.RGBA{R: r, G: g, B: b, A: 255})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bumpMap
|
||||||
|
}
|
||||||
@@ -49,6 +49,9 @@ func main() {
|
|||||||
heightmapImg := &canvas.Image{
|
heightmapImg := &canvas.Image{
|
||||||
FillMode: canvas.ImageFillContain,
|
FillMode: canvas.ImageFillContain,
|
||||||
}
|
}
|
||||||
|
bumpmapImg := &canvas.Image{
|
||||||
|
FillMode: canvas.ImageFillContain,
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize slices to store generated map features
|
// Initialize slices to store generated map features
|
||||||
var lakes [][]image.Point
|
var lakes [][]image.Point
|
||||||
@@ -67,6 +70,7 @@ func main() {
|
|||||||
appConfigDir := filepath.Join(configDir, "rpgcitymakerreborn")
|
appConfigDir := filepath.Join(configDir, "rpgcitymakerreborn")
|
||||||
canvasPath := filepath.Join(appConfigDir, "canvas.png")
|
canvasPath := filepath.Join(appConfigDir, "canvas.png")
|
||||||
heightmapPath := filepath.Join(appConfigDir, "heightmap.png")
|
heightmapPath := filepath.Join(appConfigDir, "heightmap.png")
|
||||||
|
bumpmapPath := filepath.Join(appConfigDir, "bumpmap.png")
|
||||||
|
|
||||||
// Save settings and images on window close
|
// Save settings and images on window close
|
||||||
w.SetOnClosed(func() {
|
w.SetOnClosed(func() {
|
||||||
@@ -97,6 +101,18 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if bumpmapImg.Image != nil {
|
||||||
|
bumpmapFile, err := os.Create(bumpmapPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to create bumpmap file:", err)
|
||||||
|
} else {
|
||||||
|
defer bumpmapFile.Close()
|
||||||
|
if err := png.Encode(bumpmapFile, bumpmapImg.Image); err != nil {
|
||||||
|
log.Println("Failed to encode bumpmap image:", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Load previously saved images
|
// Load previously saved images
|
||||||
@@ -118,6 +134,15 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bumpmapFile, err := os.Open(bumpmapPath)
|
||||||
|
if err == nil {
|
||||||
|
defer bumpmapFile.Close()
|
||||||
|
img, err := png.Decode(bumpmapFile)
|
||||||
|
if err == nil {
|
||||||
|
bumpmapImg.Image = img
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Generate initial images if none are loaded
|
// Generate initial images if none are loaded
|
||||||
if canvasImg.Image == nil || heightmapImg.Image == nil {
|
if canvasImg.Image == nil || heightmapImg.Image == nil {
|
||||||
|
|
||||||
@@ -177,7 +202,11 @@ func main() {
|
|||||||
// Step 10: Applying Roughness
|
// Step 10: Applying Roughness
|
||||||
compositeImg := ApplyRoughness(flattenedHeightmap, settings.Roughness)
|
compositeImg := ApplyRoughness(flattenedHeightmap, settings.Roughness)
|
||||||
|
|
||||||
|
// Step 11: Generating Bump Map
|
||||||
|
bumpMap := GenerateBumpMap(compositeImg.(*image.RGBA), settings.Width, settings.Height, 0.10)
|
||||||
|
|
||||||
heightmapImg.Image = compositeImg
|
heightmapImg.Image = compositeImg
|
||||||
|
bumpmapImg.Image = bumpMap
|
||||||
|
|
||||||
canvasImg.Image = finalImage
|
canvasImg.Image = finalImage
|
||||||
|
|
||||||
@@ -388,6 +417,9 @@ func main() {
|
|||||||
exportHeightmapBtn := widget.NewButton("Export Heightmap", func() {
|
exportHeightmapBtn := widget.NewButton("Export Heightmap", func() {
|
||||||
showSaveDialog(w, heightmapImg.Image, settings)
|
showSaveDialog(w, heightmapImg.Image, settings)
|
||||||
})
|
})
|
||||||
|
exportBumpmapBtn := widget.NewButton("Export Bump Map", func() {
|
||||||
|
showSaveDialog(w, bumpmapImg.Image, settings)
|
||||||
|
})
|
||||||
exportMasksBtn := widget.NewButton("Export Masks", func() {
|
exportMasksBtn := widget.NewButton("Export Masks", func() {
|
||||||
showMasksSaveDialog(w, canvasImg.Image, heightmapImg.Image, settings, lakes, riverPixels, treePixels, roadPixels, bridgePixels, buildingPixels, buildings)
|
showMasksSaveDialog(w, canvasImg.Image, heightmapImg.Image, settings, lakes, riverPixels, treePixels, roadPixels, bridgePixels, buildingPixels, buildings)
|
||||||
})
|
})
|
||||||
@@ -502,13 +534,23 @@ func main() {
|
|||||||
})
|
})
|
||||||
treePixels = GenerateTrees(finalImage, allWaterPixels, roadPixels, buildingPixels, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, seedProvider.Next())
|
treePixels = GenerateTrees(finalImage, allWaterPixels, roadPixels, buildingPixels, settings.MinTreeSize, settings.MaxTreeSize, settings.TreeCoverage, settings.TreeClumpiness, seedProvider.Next())
|
||||||
|
|
||||||
// Step 10: Finalizing Images
|
// Step 10: Generating Bump Map
|
||||||
|
currentStep++
|
||||||
|
fyne.Do(func() {
|
||||||
|
progressBar.SetText(fmt.Sprintf("Step %d/%d: Generating Bump Map", currentStep, steps))
|
||||||
|
progressBar.SetValue(float64(currentStep) / float64(steps))
|
||||||
|
})
|
||||||
|
bumpMap := GenerateBumpMap(compositeImg.(*image.RGBA), settings.Width, settings.Height, 0.10)
|
||||||
|
|
||||||
|
// Step 11: Finalizing Images
|
||||||
currentStep++
|
currentStep++
|
||||||
fyne.Do(func() {
|
fyne.Do(func() {
|
||||||
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))
|
||||||
heightmapImg.Image = compositeImg
|
heightmapImg.Image = compositeImg
|
||||||
heightmapImg.Refresh()
|
heightmapImg.Refresh()
|
||||||
|
bumpmapImg.Image = bumpMap
|
||||||
|
bumpmapImg.Refresh()
|
||||||
canvasImg.Image = finalImage
|
canvasImg.Image = finalImage
|
||||||
canvasImg.Refresh()
|
canvasImg.Refresh()
|
||||||
|
|
||||||
@@ -884,6 +926,7 @@ func main() {
|
|||||||
widget.NewSeparator(),
|
widget.NewSeparator(),
|
||||||
exportCanvasBtn,
|
exportCanvasBtn,
|
||||||
exportHeightmapBtn,
|
exportHeightmapBtn,
|
||||||
|
exportBumpmapBtn,
|
||||||
exportMasksBtn,
|
exportMasksBtn,
|
||||||
))
|
))
|
||||||
// Create the main layout using a horizontal split
|
// Create the main layout using a horizontal split
|
||||||
@@ -900,9 +943,10 @@ func main() {
|
|||||||
tabs,
|
tabs,
|
||||||
)
|
)
|
||||||
|
|
||||||
right := container.NewGridWithRows(2,
|
right := container.NewGridWithRows(3,
|
||||||
canvasImg,
|
canvasImg,
|
||||||
heightmapImg,
|
heightmapImg,
|
||||||
|
bumpmapImg,
|
||||||
)
|
)
|
||||||
|
|
||||||
split := container.NewHSplit(
|
split := container.NewHSplit(
|
||||||
|
|||||||
Reference in New Issue
Block a user