226 lines
5.9 KiB
Go
226 lines
5.9 KiB
Go
|
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))
|
||
|
}
|
||
|
}
|
||
|
}
|