Compare commits
2 Commits
37589d5396
...
27afe594fe
Author | SHA1 | Date | |
---|---|---|---|
27afe594fe | |||
dcd4979a6f |
@ -1,70 +0,0 @@
|
|||||||
// author: Jacky Boen
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/veandco/go-sdl2/img"
|
|
||||||
"github.com/veandco/go-sdl2/sdl"
|
|
||||||
)
|
|
||||||
|
|
||||||
var winTitle string = "Go-SDL2 Texture"
|
|
||||||
var winWidth, winHeight int32 = 800, 600
|
|
||||||
var imageName string = "../../assets/test.png"
|
|
||||||
|
|
||||||
func run() int {
|
|
||||||
var window *sdl.Window
|
|
||||||
var renderer *sdl.Renderer
|
|
||||||
var texture *sdl.Texture
|
|
||||||
var src, dst sdl.Rect
|
|
||||||
var err error
|
|
||||||
|
|
||||||
window, err = sdl.CreateWindow(winTitle, sdl.WINDOWPOS_UNDEFINED, sdl.WINDOWPOS_UNDEFINED,
|
|
||||||
winWidth, winHeight, sdl.WINDOW_SHOWN)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
defer window.Destroy()
|
|
||||||
|
|
||||||
renderer, err = sdl.CreateRenderer(window, -1, sdl.RENDERER_ACCELERATED)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to create renderer: %s\n", err)
|
|
||||||
return 2
|
|
||||||
}
|
|
||||||
defer renderer.Destroy()
|
|
||||||
|
|
||||||
image, err := img.Load(imageName)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to load PNG: %s\n", err)
|
|
||||||
return 3
|
|
||||||
}
|
|
||||||
defer image.Free()
|
|
||||||
|
|
||||||
texture, err = renderer.CreateTextureFromSurface(image)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "Failed to create texture: %s\n", err)
|
|
||||||
return 4
|
|
||||||
}
|
|
||||||
defer texture.Destroy()
|
|
||||||
|
|
||||||
src = sdl.Rect{0, 0, 512, 512}
|
|
||||||
dst = sdl.Rect{100, 50, 512, 512}
|
|
||||||
|
|
||||||
renderer.Clear()
|
|
||||||
renderer.SetDrawColor(255, 0, 0, 255)
|
|
||||||
renderer.FillRect(&sdl.Rect{0, 0, int32(winWidth), int32(winHeight)})
|
|
||||||
renderer.Copy(texture, &src, &dst)
|
|
||||||
renderer.Present()
|
|
||||||
|
|
||||||
sdl.PollEvent()
|
|
||||||
sdl.Delay(2000)
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
os.Exit(run())
|
|
||||||
}
|
|
BIN
cmd/tins2020/res/images/cactus_short_NE.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
cmd/tins2020/res/images/cactus_short_NW.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
cmd/tins2020/res/images/cactus_short_SE.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
cmd/tins2020/res/images/cactus_short_SW.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
cmd/tins2020/res/images/cactus_tall_NE.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
cmd/tins2020/res/images/cactus_tall_NW.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
cmd/tins2020/res/images/cactus_tall_SE.png
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
cmd/tins2020/res/images/cactus_tall_SW.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
cmd/tins2020/res/images/grass_NE.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
cmd/tins2020/res/images/grass_NW.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
cmd/tins2020/res/images/grass_SE.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
cmd/tins2020/res/images/grass_SW.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
cmd/tins2020/res/images/grass_leafs_NE.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
cmd/tins2020/res/images/grass_leafs_NW.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
cmd/tins2020/res/images/grass_leafs_SE.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
cmd/tins2020/res/images/grass_leafs_SW.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
cmd/tins2020/res/images/plant_bushLarge_NE.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
cmd/tins2020/res/images/plant_bushLarge_NW.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
cmd/tins2020/res/images/plant_bushLarge_SE.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
cmd/tins2020/res/images/plant_bushLarge_SW.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
cmd/tins2020/res/images/plant_bushSmall_NE.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
cmd/tins2020/res/images/plant_bushSmall_NW.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
cmd/tins2020/res/images/plant_bushSmall_SE.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
cmd/tins2020/res/images/plant_bushSmall_SW.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
BIN
cmd/tins2020/res/images/tree_fat_NE.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
cmd/tins2020/res/images/tree_fat_NW.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
cmd/tins2020/res/images/tree_fat_SE.png
Normal file
After Width: | Height: | Size: 2.9 KiB |
BIN
cmd/tins2020/res/images/tree_fat_SW.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
cmd/tins2020/res/images/tree_pineDefaultA_NE.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
cmd/tins2020/res/images/tree_pineDefaultA_NW.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
cmd/tins2020/res/images/tree_pineDefaultA_SE.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
BIN
cmd/tins2020/res/images/tree_pineDefaultA_SW.png
Normal file
After Width: | Height: | Size: 3.9 KiB |
@ -1,41 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/veandco/go-sdl2/sdl"
|
|
||||||
"opslag.de/schobers/tins2020"
|
|
||||||
)
|
|
||||||
|
|
||||||
type terrainRenderer struct {
|
|
||||||
terrain *tins2020.Map
|
|
||||||
center tins2020.PointF
|
|
||||||
zoom float32
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTerrainRenderer(terrain *tins2020.Map) *terrainRenderer {
|
|
||||||
return &terrainRenderer{terrain: terrain}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *terrainRenderer) Render(ctx *tins2020.Context) {
|
|
||||||
tempToTexture := func(temp float64) *tins2020.Texture {
|
|
||||||
if temp < .2 {
|
|
||||||
return ctx.Textures.Texture("snow")
|
|
||||||
}
|
|
||||||
if temp > .8 {
|
|
||||||
return ctx.Textures.Texture("dirt")
|
|
||||||
}
|
|
||||||
return ctx.Textures.Texture("grass")
|
|
||||||
}
|
|
||||||
|
|
||||||
// horizontal: [191, 321) = 130
|
|
||||||
// vertical: [267,332) = 65
|
|
||||||
|
|
||||||
for y := int32(0); y < 10; y++ {
|
|
||||||
left := -y * 65
|
|
||||||
top := y * 32
|
|
||||||
for x := int32(0); x < 10; x++ {
|
|
||||||
temp := r.terrain.Temp.Value(int(x), int(y))
|
|
||||||
text := tempToTexture(temp)
|
|
||||||
text.Copy(ctx.Renderer, &sdl.Rect{X: left + x*65, Y: top + x*32, W: 512, H: 512})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -28,7 +28,7 @@ func run() error {
|
|||||||
}
|
}
|
||||||
defer sdl.Quit()
|
defer sdl.Quit()
|
||||||
|
|
||||||
logSDLVersion()
|
// logSDLVersion()
|
||||||
|
|
||||||
if err := ttf.Init(); err != nil {
|
if err := ttf.Init(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -69,24 +69,66 @@ func run() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = ctx.Textures.Load(
|
err = ctx.Textures.Load(
|
||||||
"dirt", "images/dirt.png",
|
"tile-dirt", "images/tile_dirt.png",
|
||||||
"grass", "images/grass.png",
|
"tile-grass", "images/tile_grass.png",
|
||||||
"snow", "images/snow.png",
|
"tile-snow", "images/tile_snow.png",
|
||||||
|
|
||||||
|
"cactus-small-1", "images/cactus_short_NE.png",
|
||||||
|
"cactus-small-2", "images/cactus_short_NW.png",
|
||||||
|
"cactus-small-3", "images/cactus_short_SW.png",
|
||||||
|
"cactus-small-4", "images/cactus_short_SE.png",
|
||||||
|
|
||||||
|
"cactus-tall-1", "images/cactus_tall_NE.png",
|
||||||
|
"cactus-tall-2", "images/cactus_tall_NW.png",
|
||||||
|
"cactus-tall-3", "images/cactus_tall_SW.png",
|
||||||
|
"cactus-tall-4", "images/cactus_tall_SE.png",
|
||||||
|
|
||||||
|
"tree-fat-1", "images/tree_fat_NE.png",
|
||||||
|
"tree-fat-2", "images/tree_fat_NW.png",
|
||||||
|
"tree-fat-3", "images/tree_fat_SW.png",
|
||||||
|
"tree-fat-4", "images/tree_fat_SE.png",
|
||||||
|
|
||||||
|
"tree-pine-1", "images/tree_pineDefaultA_NE.png",
|
||||||
|
"tree-pine-2", "images/tree_pineDefaultA_NW.png",
|
||||||
|
"tree-pine-3", "images/tree_pineDefaultA_SW.png",
|
||||||
|
"tree-pine-4", "images/tree_pineDefaultA_SE.png",
|
||||||
|
|
||||||
|
"grass-small-1", "images/grass_NE.png",
|
||||||
|
"grass-small-2", "images/grass_NW.png",
|
||||||
|
"grass-small-3", "images/grass_SW.png",
|
||||||
|
"grass-small-4", "images/grass_SE.png",
|
||||||
|
|
||||||
|
"grass-leafs-1", "images/grass_leafs_NE.png",
|
||||||
|
"grass-leafs-2", "images/grass_leafs_NW.png",
|
||||||
|
"grass-leafs-3", "images/grass_leafs_SW.png",
|
||||||
|
"grass-leafs-4", "images/grass_leafs_SE.png",
|
||||||
|
|
||||||
|
"bush-small-1", "images/plant_bushSmall_NE.png",
|
||||||
|
"bush-small-2", "images/plant_bushSmall_NW.png",
|
||||||
|
"bush-small-3", "images/plant_bushSmall_SW.png",
|
||||||
|
"bush-small-4", "images/plant_bushSmall_SE.png",
|
||||||
|
|
||||||
|
"bush-large-1", "images/plant_bushLarge_NE.png",
|
||||||
|
"bush-large-2", "images/plant_bushLarge_NW.png",
|
||||||
|
"bush-large-3", "images/plant_bushLarge_SW.png",
|
||||||
|
"bush-large-4", "images/plant_bushLarge_SE.png",
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
game := tins2020.NewGame()
|
game := tins2020.NewGame()
|
||||||
tr := newTerrainRenderer(game.Terrain)
|
control := tins2020.NewTerrainRenderer(game.Terrain)
|
||||||
|
err = control.Init(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
running := true
|
for {
|
||||||
for running {
|
|
||||||
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
|
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
|
||||||
switch e := event.(type) {
|
switch e := event.(type) {
|
||||||
case *sdl.QuitEvent:
|
case *sdl.QuitEvent:
|
||||||
println("Quit")
|
ctx.Quit()
|
||||||
running = false
|
|
||||||
break
|
break
|
||||||
case *sdl.WindowEvent:
|
case *sdl.WindowEvent:
|
||||||
switch e.Event {
|
switch e.Event {
|
||||||
@ -94,17 +136,22 @@ func run() error {
|
|||||||
x, y := window.GetPosition()
|
x, y := window.GetPosition()
|
||||||
ctx.Settings.Window.Location = &tins2020.Point{X: x, Y: y}
|
ctx.Settings.Window.Location = &tins2020.Point{X: x, Y: y}
|
||||||
}
|
}
|
||||||
|
case *sdl.KeyboardEvent:
|
||||||
|
switch e.Keysym.Sym {
|
||||||
|
case sdl.K_ESCAPE:
|
||||||
|
ctx.Quit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
control.Handle(ctx, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.ShouldQuit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
renderer.SetDrawColor(0, 0, 0, 255)
|
renderer.SetDrawColor(0, 0, 0, 255)
|
||||||
renderer.Clear()
|
renderer.Clear()
|
||||||
// renderer.SetDrawColor(255, 0, 0, 255)
|
control.Render(ctx)
|
||||||
// renderer.FillRect(&sdl.Rect{0, 0, 200, 200})
|
|
||||||
// ctx.Textures.Texture("grass").Copy(renderer, &sdl.Rect{X: 200, Y: 100, W: 456, H: 356})
|
|
||||||
// ctx.Textures.Texture("dirt").Copy(renderer, &sdl.Rect{X: 143, Y: 122, W: 456, H: 356})
|
|
||||||
// ctx.Textures.Texture("snow").Copy(renderer, &sdl.Rect{X: 257, Y: 122, W: 456, H: 356})
|
|
||||||
tr.Render(ctx)
|
|
||||||
renderer.Present()
|
renderer.Present()
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
15
context.go
@ -11,6 +11,7 @@ type Context struct {
|
|||||||
Resources Resources
|
Resources Resources
|
||||||
Textures Textures
|
Textures Textures
|
||||||
Settings Settings
|
Settings Settings
|
||||||
|
ShouldQuit bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContext(res *rice.Box) (*Context, error) {
|
func NewContext(res *rice.Box) (*Context, error) {
|
||||||
@ -26,15 +27,17 @@ func NewContext(res *rice.Box) (*Context, error) {
|
|||||||
return ctx, nil
|
return ctx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) Init(renderer *sdl.Renderer) {
|
|
||||||
c.Renderer = renderer
|
|
||||||
c.Fonts.Init(c.Resources.Copy())
|
|
||||||
c.Textures.Init(renderer, c.Resources.Copy())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) Destroy() {
|
func (c *Context) Destroy() {
|
||||||
c.Fonts.Destroy()
|
c.Fonts.Destroy()
|
||||||
c.Resources.Destroy()
|
c.Resources.Destroy()
|
||||||
c.Textures.Destroy()
|
c.Textures.Destroy()
|
||||||
c.Settings.Store()
|
c.Settings.Store()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) Init(renderer *sdl.Renderer) {
|
||||||
|
c.Renderer = renderer
|
||||||
|
c.Fonts.Init(c.Resources.Copy())
|
||||||
|
c.Textures.Init(renderer, c.Resources.Copy())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) Quit() { c.ShouldQuit = true }
|
||||||
|
62
game.go
@ -13,33 +13,69 @@ type Game struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Map struct {
|
type Map struct {
|
||||||
Temp *NoiseMap
|
Temp NoiseMap
|
||||||
Humid *NoiseMap
|
Humid NoiseMap
|
||||||
|
Variant NoiseMap
|
||||||
|
PlaceX NoiseMap // displacement map of props
|
||||||
|
PlaceY NoiseMap
|
||||||
}
|
}
|
||||||
|
|
||||||
type NoiseMap struct {
|
type NoiseMap interface {
|
||||||
|
Value(x, y int32) float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNoiseMap(seed int64) NoiseMap {
|
||||||
|
return &noiseMap{
|
||||||
|
noise: noise.NewPerlin(seed),
|
||||||
|
alpha: 2,
|
||||||
|
beta: 4,
|
||||||
|
harmonics: 2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRandomNoiseMap(seed int64) NoiseMap {
|
||||||
|
return &randomNoiseMap{noise.NewPerlin(seed)}
|
||||||
|
}
|
||||||
|
|
||||||
|
type noiseMap struct {
|
||||||
noise *noise.Perlin
|
noise *noise.Perlin
|
||||||
alpha, beta float64
|
alpha, beta float64
|
||||||
harmonics int
|
harmonics int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewNoiseMap(seed int64) *NoiseMap {
|
func clipNormalized(x float64) float64 {
|
||||||
return &NoiseMap{
|
if x < 0 {
|
||||||
noise: noise.NewPerlin(seed),
|
return 0
|
||||||
alpha: 2,
|
|
||||||
beta: 2.13,
|
|
||||||
harmonics: 4,
|
|
||||||
}
|
}
|
||||||
|
if x > 1 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return x
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *NoiseMap) Value(x, y int) float64 {
|
// Value generates the noise value for an x/y pair. The range of the output is approximately [-1.325825214,1.325825214] (for 4 harmonics).
|
||||||
return m.noise.Noise2D(float64(x), float64(y), m.alpha, m.beta, m.harmonics)
|
func (m *noiseMap) Value(x, y int32) float64 {
|
||||||
|
value := m.noise.Noise2D(float64(x)*.01, float64(y)*.01, m.alpha, m.beta, m.harmonics)*.565 + .5
|
||||||
|
return clipNormalized(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
type randomNoiseMap struct {
|
||||||
|
*noise.Perlin
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value generates the noise value for an x/y pair. The range of the output is approximately [-1.325825214,1.325825214] (for 4 harmonics).
|
||||||
|
func (m randomNoiseMap) Value(x, y int32) float64 {
|
||||||
|
value := m.Noise2D(float64(x)*.53, float64(y)*.53, 1.01, 2, 2)*.5 + .5
|
||||||
|
return clipNormalized(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGame() *Game {
|
func NewGame() *Game {
|
||||||
terrain := &Map{
|
terrain := &Map{
|
||||||
Temp: NewNoiseMap(rand.Int63()),
|
Temp: NewNoiseMap(rand.Int63()),
|
||||||
Humid: NewNoiseMap(rand.Int63()),
|
Humid: NewNoiseMap(rand.Int63()),
|
||||||
|
Variant: NewRandomNoiseMap(rand.Int63()),
|
||||||
|
PlaceX: NewRandomNoiseMap(rand.Int63()),
|
||||||
|
PlaceY: NewRandomNoiseMap(rand.Int63()),
|
||||||
}
|
}
|
||||||
return &Game{
|
return &Game{
|
||||||
Terrain: terrain,
|
Terrain: terrain,
|
||||||
@ -48,10 +84,6 @@ func NewGame() *Game {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type PointF struct {
|
|
||||||
X, Y float32
|
|
||||||
}
|
|
||||||
|
|
||||||
type Flower struct {
|
type Flower struct {
|
||||||
Location PointF
|
Location PointF
|
||||||
}
|
}
|
||||||
|
16
point.go
@ -1,6 +1,18 @@
|
|||||||
package tins2020
|
package tins2020
|
||||||
|
|
||||||
type Point struct {
|
type Point struct {
|
||||||
X int32
|
X, Y int32
|
||||||
Y int32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p Point) Add(q Point) Point { return Pt(p.X+q.X, p.Y+q.Y) }
|
||||||
|
|
||||||
|
type PointF struct {
|
||||||
|
X, Y float32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p PointF) Add(q PointF) PointF { return PtF(p.X+q.X, p.Y+q.Y) }
|
||||||
|
|
||||||
|
func (p PointF) Sub(q PointF) PointF { return PtF(p.X-q.X, p.Y-q.Y) }
|
||||||
|
|
||||||
|
func Pt(x, y int32) Point { return Point{x, y} }
|
||||||
|
func PtF(x, y float32) PointF { return PointF{x, y} }
|
||||||
|
9
renderer.go
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
import "github.com/veandco/go-sdl2/sdl"
|
||||||
|
|
||||||
|
type Control interface {
|
||||||
|
Init(*Context) error
|
||||||
|
Handle(*Context, sdl.Event)
|
||||||
|
Render(*Context)
|
||||||
|
}
|
225
terrainrenderer.go
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
package tins2020
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"opslag.de/schobers/geom"
|
||||||
|
|
||||||
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
|
)
|
||||||
|
|
||||||
|
type terrainRenderer struct {
|
||||||
|
terrain *Map
|
||||||
|
hover PointF
|
||||||
|
project projection
|
||||||
|
|
||||||
|
interact interaction
|
||||||
|
}
|
||||||
|
|
||||||
|
type interaction struct {
|
||||||
|
mousePos Point
|
||||||
|
mouseLeftDown bool
|
||||||
|
mouseDrag *Point
|
||||||
|
}
|
||||||
|
|
||||||
|
type projection struct {
|
||||||
|
center PointF
|
||||||
|
zoom float32
|
||||||
|
zoomInv float32
|
||||||
|
|
||||||
|
tileScreenDelta PointF
|
||||||
|
tileScreenDeltaInv PointF
|
||||||
|
tileScreenOffset Point
|
||||||
|
tileScreenSize Point
|
||||||
|
windowCenter Point
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProjection() projection {
|
||||||
|
return projection{zoom: 1, tileScreenDelta: PtF(65, 32), tileScreenDeltaInv: PtF(1./65, 1./32)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) update(renderer *sdl.Renderer) {
|
||||||
|
p.zoomInv = 1 / p.zoom
|
||||||
|
|
||||||
|
p.tileScreenOffset = Pt(int32(p.zoomInv*256), int32(p.zoomInv*300))
|
||||||
|
p.tileScreenSize = Pt(int32(p.zoomInv*512), int32(p.zoomInv*512))
|
||||||
|
|
||||||
|
windowW, windowH, err := renderer.GetOutputSize()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
p.windowCenter = Pt(windowW/2, windowH/2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) mapToScreen(x, y int32) Point {
|
||||||
|
return p.mapToScreenF(float32(x), float32(y))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) mapToScreenF(x, y float32) Point {
|
||||||
|
translated := PtF(x-p.center.X, y-p.center.Y)
|
||||||
|
return Pt(p.windowCenter.X+int32((translated.X-translated.Y)*65*p.zoomInv), p.windowCenter.Y+int32((translated.X+translated.Y)*32*p.zoomInv))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) screenToMapRel(x, y int32) PointF {
|
||||||
|
normX := p.zoom * float32(x)
|
||||||
|
normY := p.zoom * float32(y)
|
||||||
|
return PtF(.5*(p.tileScreenDeltaInv.X*normX+p.tileScreenDeltaInv.Y*normY), .5*(-p.tileScreenDeltaInv.X*normX+p.tileScreenDeltaInv.Y*normY))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) screenToMap(x, y int32) PointF {
|
||||||
|
pos := p.screenToMapRel(x-p.windowCenter.X, y-p.windowCenter.Y)
|
||||||
|
return p.center.Add(pos)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *projection) screenToTileRect(pos Point) *sdl.Rect {
|
||||||
|
return &sdl.Rect{X: pos.X - p.tileScreenOffset.X, Y: pos.Y - p.tileScreenOffset.Y, W: p.tileScreenSize.X, H: p.tileScreenSize.Y}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTerrainRenderer(terrain *Map) Control {
|
||||||
|
return &terrainRenderer{terrain: terrain, project: newProjection()}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *terrainRenderer) Init(ctx *Context) error {
|
||||||
|
r.project.update(ctx.Renderer)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *terrainRenderer) Handle(ctx *Context, event sdl.Event) {
|
||||||
|
switch e := event.(type) {
|
||||||
|
case *sdl.MouseButtonEvent:
|
||||||
|
if e.Button == sdl.BUTTON_LEFT {
|
||||||
|
r.interact.mouseLeftDown = e.Type == sdl.MOUSEBUTTONDOWN
|
||||||
|
if r.interact.mouseLeftDown && r.interact.mouseDrag == nil {
|
||||||
|
r.interact.mouseDrag = &Point{e.X, e.Y}
|
||||||
|
fmt.Println("Drag start", r.interact.mouseDrag)
|
||||||
|
} else if !r.interact.mouseLeftDown && r.interact.mouseDrag != nil {
|
||||||
|
fmt.Println("Drag end", r.interact.mouseDrag)
|
||||||
|
r.interact.mouseDrag = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *sdl.MouseMotionEvent:
|
||||||
|
r.hover = r.project.screenToMap(e.X, e.Y)
|
||||||
|
if r.interact.mouseDrag != nil {
|
||||||
|
fmt.Println("Dragging...", r.project.center)
|
||||||
|
r.project.center = r.project.center.Sub(r.project.screenToMapRel(e.X-r.interact.mouseDrag.X, e.Y-r.interact.mouseDrag.Y))
|
||||||
|
r.project.update(ctx.Renderer)
|
||||||
|
r.interact.mouseDrag = &Point{e.X, e.Y}
|
||||||
|
}
|
||||||
|
case *sdl.MouseWheelEvent:
|
||||||
|
if e.Y > 0 {
|
||||||
|
r.project.zoom *= .5
|
||||||
|
r.project.update(ctx.Renderer)
|
||||||
|
} else if e.Y < 0 {
|
||||||
|
r.project.zoom *= 2
|
||||||
|
r.project.update(ctx.Renderer)
|
||||||
|
}
|
||||||
|
case *sdl.WindowEvent:
|
||||||
|
if e.Event == sdl.WINDOWEVENT_RESIZED {
|
||||||
|
r.project.update(ctx.Renderer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *terrainRenderer) Render(ctx *Context) {
|
||||||
|
toTileTexture := func(temp, humid float64) *Texture {
|
||||||
|
if temp < .35 {
|
||||||
|
return ctx.Textures.Texture("tile-snow")
|
||||||
|
}
|
||||||
|
if temp > .65 {
|
||||||
|
return ctx.Textures.Texture("tile-dirt")
|
||||||
|
}
|
||||||
|
return ctx.Textures.Texture("tile-grass")
|
||||||
|
}
|
||||||
|
|
||||||
|
variantToInt := func(variant float64) int {
|
||||||
|
if variant < .25 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if variant < .5 {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
if variant < .75 {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
if variant < 1 {
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
variantToTexture := func(format string, variant float64) *Texture {
|
||||||
|
textName := fmt.Sprintf(format, variantToInt(variant))
|
||||||
|
return ctx.Textures.Texture(textName)
|
||||||
|
}
|
||||||
|
|
||||||
|
stretch := func(x, from, to float64) float64 { return (x - from) * 1 / (to - from) }
|
||||||
|
|
||||||
|
toPropTexture := func(temp, humid, variant float64) *Texture {
|
||||||
|
if temp < .35 {
|
||||||
|
if humid < .2 {
|
||||||
|
return nil
|
||||||
|
} else if humid < .7 {
|
||||||
|
return variantToTexture("bush-small-%d", variant*5)
|
||||||
|
}
|
||||||
|
return variantToTexture("tree-pine-%d", variant*5)
|
||||||
|
}
|
||||||
|
if temp > .65 {
|
||||||
|
if humid < .2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if humid < .7 {
|
||||||
|
return variantToTexture("cactus-short-%d", variant*7)
|
||||||
|
}
|
||||||
|
return variantToTexture("cactus-tall-%d", variant*2)
|
||||||
|
}
|
||||||
|
if humid < .2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
multiplier := 1 - stretch(humid, 0.2, 1)
|
||||||
|
if variant < .5 {
|
||||||
|
return variantToTexture("tree-fat-%d", stretch(variant, 0, .5)*multiplier*3)
|
||||||
|
} else if variant < .8 {
|
||||||
|
return variantToTexture("grass-small-%d", stretch(variant, .5, .8)*multiplier*2)
|
||||||
|
}
|
||||||
|
return variantToTexture("bush-large-%d", stretch(variant, .8, 1)*multiplier)
|
||||||
|
}
|
||||||
|
|
||||||
|
// horizontal: [191, 321) = 130
|
||||||
|
// vertical: [267,332) = 65
|
||||||
|
|
||||||
|
hover := Pt(int32(geom.Round32(r.hover.X)), int32(geom.Round32(r.hover.Y)))
|
||||||
|
for y := int32(-100); y < 100; y++ {
|
||||||
|
for x := int32(-100); x < 100; x++ {
|
||||||
|
if x == hover.X && y == hover.Y {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
temp := r.terrain.Temp.Value(x, y)
|
||||||
|
humid := r.terrain.Humid.Value(x, y)
|
||||||
|
text := toTileTexture(temp, humid)
|
||||||
|
pos := r.project.mapToScreen(x, y)
|
||||||
|
text.Copy(ctx.Renderer, r.project.screenToTileRect(pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := int32(-100); y < 100; y++ {
|
||||||
|
for x := int32(-100); x < 100; x++ {
|
||||||
|
variant := r.terrain.Variant.Value(x, y)
|
||||||
|
if variant < -1 || variant > 1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
temp := r.terrain.Temp.Value(x, y)
|
||||||
|
humid := r.terrain.Humid.Value(x, y)
|
||||||
|
text := toPropTexture(temp, humid, variant)
|
||||||
|
if text == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
placeX, placeY := r.terrain.PlaceX.Value(x, y), r.terrain.PlaceY.Value(x, y)
|
||||||
|
pos := r.project.mapToScreenF(float32(x)-.2+float32(.8*placeX-.4), float32(y)-.2+float32(.8*placeY-.4))
|
||||||
|
|
||||||
|
text.Copy(ctx.Renderer, r.project.screenToTileRect(pos))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|