diff --git a/community/procedural-terrain-1d/README.md b/community/procedural-terrain-1d/README.md new file mode 100644 index 0000000..d803a09 --- /dev/null +++ b/community/procedural-terrain-1d/README.md @@ -0,0 +1,8 @@ +# Procedura 1D terrain generator + +This is a demo of a 1D terrain generator using [Perlin noise](https://en.wikipedia.org/wiki/Perlin_noise) algorithm. +Press *space* to generate a random terrain. + +Uses [Go-Perlin](https://github.com/aquilax/go-perlin). + +Texture by [hh316](https://hhh316.deviantart.com/art/Seamless-stone-cliff-face-mountain-texture-377076626). diff --git a/community/procedural-terrain-1d/main.go b/community/procedural-terrain-1d/main.go new file mode 100644 index 0000000..242ee43 --- /dev/null +++ b/community/procedural-terrain-1d/main.go @@ -0,0 +1,106 @@ +package main + +import ( + "image" + "log" + "math/rand" + "os" + "time" + + _ "image/jpeg" + + perlin "github.com/aquilax/go-perlin" + "github.com/faiface/pixel" + "github.com/faiface/pixel/pixelgl" + "github.com/svera/pixel/imdraw" + "golang.org/x/image/colornames" +) + +const ( + width = 800 + height = 600 + // Top of the mountain must be around the half of the screen height + verticalOffset = height / 2 + // Perlin noise provides variations in values between -1 and 1, + // we multiply those so they're visible on screen + scale = 100 + waveLength = 100 + alpha = 2. + beta = 2. + n = 3 + maximumSeedValue = 100 +) + +func init() { + rand.Seed(time.Now().UTC().UnixNano()) +} + +func main() { + pixelgl.Run(run) +} + +func run() { + cfg := pixelgl.WindowConfig{ + Title: "Procedural terrain 1D", + Bounds: pixel.R(0, 0, width, height), + VSync: true, + } + win, err := pixelgl.NewWindow(cfg) + if err != nil { + panic(err) + } + pic, err := loadPicture("stone.jpg") + if err != nil { + panic(err) + } + imd := imdraw.New(pic) + + drawTerrain(win, imd) + + for !win.Closed() { + if win.JustPressed(pixelgl.KeySpace) { + drawTerrain(win, imd) + } + win.Update() + } +} + +func loadPicture(path string) (pixel.Picture, error) { + file, err := os.Open(path) + if err != nil { + return nil, err + } + defer file.Close() + img, _, err := image.Decode(file) + if err != nil { + return nil, err + } + return pixel.PictureDataFromImage(img), nil +} + +func drawTerrain(win *pixelgl.Window, imd *imdraw.IMDraw) { + var seed = rand.Int63n(maximumSeedValue) + p := perlin.NewPerlin(alpha, beta, n, seed) + + imd.Clear() + win.Clear(colornames.Skyblue) + for x := 0.; x < width; x++ { + y := p.Noise1D(x/waveLength)*scale + verticalOffset + log.Printf("%f\n", y) + renderTexturedLine(x, y, imd) + } + imd.Draw(win) +} + +// Render a textured line in position x with a height y. +// Note that the textured line is just a 1 px width rectangle. +// We push the opposite vertices of that rectangle and specify the points of the +// texture we want to apply to them. Pixel will fill the rest of the rectangle interpolating the texture. +func renderTexturedLine(x, y float64, imd *imdraw.IMDraw) { + imd.Intensity = 1. + imd.Picture = pixel.V(x, 0) + imd.Push(pixel.V(x, 0)) + imd.Picture = pixel.V(x+1, y) + imd.Push(pixel.V(x+1, y)) + imd.Rectangle(0) +} diff --git a/community/procedural-terrain-1d/stone.jpg b/community/procedural-terrain-1d/stone.jpg new file mode 100644 index 0000000..9899939 Binary files /dev/null and b/community/procedural-terrain-1d/stone.jpg differ