ld48-29/ld48-29.go

363 lines
9 KiB
Go

package main
import (
"runtime"
"time"
"math"
"log"
"io"
"errors"
"os"
"syscall"
"image"
"image/png"
gl "github.com/go-gl/gl"
glfw "github.com/go-gl/glfw3"
pa "code.google.com/p/portaudio-go/portaudio"
)
var _ = pa.Initialize // TODO: remove later
const (
INPUT_UP = 0
INPUT_DOWN = 1
INPUT_LEFT = 2
INPUT_RIGHT = 3
)
// iterate faster
func rerun() (err error) {
log.Println("rerun")
gopath := os.Getenv("GOPATH")
env := []string{"GOPATH=" + gopath}
args := []string{"go", "run", "ld48-29.go"}
err = syscall.Exec("/usr/local/bin/go", args, env)
log.Fatal(err)
return
}
func reexec() {
err := rerun()
if err != nil { panic(err) }
}
// glfw callbacks
func onError(err glfw.ErrorCode, desc string) {
log.Printf("%v: %v\n", err, desc)
}
func onKey(input chan int, window *glfw.Window, k glfw.Key, s int, action glfw.Action, mods glfw.ModifierKey) {
if action != glfw.Press{
return
}
switch glfw.Key(k) {
case glfw.KeyR:
if mods & glfw.ModSuper != 0 {
reexec()
}
case glfw.KeyUp:
input <- INPUT_UP
case glfw.KeyDown:
input <- INPUT_DOWN
case glfw.KeyLeft:
input <- INPUT_LEFT
case glfw.KeyRight:
input <- INPUT_RIGHT
case glfw.KeyEscape:
window.SetShouldClose(true)
default:
return
}
}
// utils
func readTexture(r io.Reader) (texId gl.Texture, err error) {
img, err := png.Decode(r)
if err != nil {
return gl.Texture(0), err
}
rgba, ok := img.(*image.NRGBA)
if !ok {
return gl.Texture(0), errors.New("not an NRGBA image")
}
texId = gl.GenTexture()
texId.Bind(gl.TEXTURE_2D)
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.TexParameterf(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
w, h := rgba.Bounds().Dx(), rgba.Bounds().Dy()
raw := make([]byte, w*h*4)
raw_stride := w * 4
if raw_stride != rgba.Stride {
return gl.Texture(0), errors.New("incompatible stride")
}
dst := len(raw) - raw_stride
for src := 0; src < len(rgba.Pix); src += rgba.Stride {
copy(raw[dst:dst+raw_stride], rgba.Pix[src:src+rgba.Stride])
dst -= raw_stride
}
lod := 0
border := 0
gl.TexImage2D(gl.TEXTURE_2D, lod, gl.RGBA, w, h, border, gl.RGBA, gl.UNSIGNED_BYTE, raw)
return
}
func spriteQuad(x int, y int, w int, h int) {
scaledSpriteQuad(x, y, w, h, 1.0)
}
func scaledSpriteQuad(x int, y int, w int, h int, scale float32) {
// TODO: set elsewhere (spritesheet property)
size := 256 // spritesheet size
unit := 16 // sprite unit size
// sprite to absolute pixel coords
// origin is as tex: bottom-left
x1 := x * unit
y1 := y * unit
x2 := x1 + w * unit
y2 := y1 + h * unit
// abs pixel to relative tex coords
rx1 := float32(x1) / float32(size)
rx2 := float32(x2) / float32(size)
ry1 := float32(y1) / float32(size)
ry2 := float32(y2) / float32(size)
// scale sprite
qsize := 1.0 * scale
// relative model coords
// sprite-centered origin, half each way
qx1 := -qsize * float32(unit * w) / 2.0
qy1 := -qsize * float32(unit * h) / 2.0
qx2 := qsize * float32(unit * w) / 2.0
qy2 := qsize * float32(unit * h) / 2.0
// draw sprite quad
gl.MatrixMode(gl.MODELVIEW)
gl.Begin(gl.QUADS)
gl.Normal3f(0, 0, 1)
gl.TexCoord2f(rx1, ry1)
gl.Vertex3f(qx1, qy1, 1.0)
gl.TexCoord2f(rx2, ry1)
gl.Vertex3f(qx2, qy1, 1.0)
gl.TexCoord2f(rx2, ry2)
gl.Vertex3f(qx2, qy2, 1.0)
gl.TexCoord2f(rx1, ry2)
gl.Vertex3f(qx1, qy2, 1.0)
gl.End()
}
func drawSprite(texture gl.Texture, x float64, y float64, a float64, list uint) {
deg := math.Mod(360 * float64(a) / (2 * math.Pi), 360.0)
gl.LoadIdentity()
texture.Bind(gl.TEXTURE_2D)
gl.Translatef(float32(x), float32(y), 0)
gl.Rotatef(float32(deg), 0.0, 0.0, 1.0);
gl.CallList(list)
}
func makeSprite(x int, y int, w int, h int) (quad uint) {
quad = gl.GenLists(1)
gl.NewList(quad, gl.COMPILE)
spriteQuad(x, y, w, h)
gl.EndList()
return
}
// main
func main() {
done := make(chan int)
input := make(chan int, 64)
go renderer(done, input)
go func() {
for {
in := <- input
log.Printf("input %d", in)
}
}()
<-done
}
// renderer
var mouseX float64
var mouseY float64
var mouseVisible bool
func renderer(done chan int, input chan int) {
runtime.LockOSThread()
glfw.SetErrorCallback(onError)
if !glfw.Init() {
panic("Can't init glfw!")
}
defer glfw.Terminate()
glfw.WindowHint(glfw.Resizable, 0)
window, err := glfw.CreateWindow(640, 480, "LD48-29", nil, nil)
if err != nil {
log.Panic(err)
}
window.SetInputMode(glfw.Cursor, glfw.CursorHidden)
onKeyClosure := func (window *glfw.Window, k glfw.Key, s int, action glfw.Action, mods glfw.ModifierKey) {
onKey(input, window, k, s, action, mods)
}
onMouseClosure := func (window *glfw.Window, x float64, y float64) {
mouseX, mouseY = x, 480 - y
mouseVisible = mouseX < 640 && mouseX >= 0 && mouseY < 480 && mouseY >= 0
}
window.SetKeyCallback(onKeyClosure)
window.SetCursorPositionCallback(onMouseClosure)
window.MakeContextCurrent()
textures, lists := setup()
defer destroy(textures)
for !window.ShouldClose() {
render(textures, lists)
window.SwapBuffers()
glfw.PollEvents()
}
done <- 1
}
func setup() (textures map[string]gl.Texture, lists map[string]uint) {
gl.Enable(gl.TEXTURE_2D)
gl.Enable(gl.DEPTH_TEST)
gl.Enable(gl.LIGHTING)
gl.Enable(gl.CULL_FACE)
gl.Enable(gl.BLEND)
gl.ClearColor(0.0, 0.0, 0.5, 0)
gl.ClearDepth(1)
gl.DepthFunc(gl.LEQUAL)
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
textures = map[string]gl.Texture{}
lists = map[string]uint{}
// load spritesheet and make sprites
img, err := os.Open("spritesheet.png")
if err != nil { log.Panic(err) }
defer img.Close()
spriteSheet, err := readTexture(img)
if err != nil { log.Panic(err) }
textures["sprites"] = spriteSheet
lists["test"] = makeSprite(0, 0, 2, 2)
lists["cursor"] = makeSprite(2, 0, 1, 1)
return
}
func destroy(textures map[string]gl.Texture) {
for _, texture := range textures {
texture.Delete()
}
}
func render(textures map[string]gl.Texture, lists map[string]uint) {
// start afresh
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
// set viewport
width := float32(640.0)
height := float32(480.0)
gl.Viewport(0, 0, int(width)*2, int(height)*2) // times 2 because HiDPI
gl.MatrixMode(gl.PROJECTION)
gl.LoadIdentity()
gl.Ortho(0, float64(width), 0, float64(height), -1.0, 1.0)
gl.MatrixMode(gl.MODELVIEW);
gl.LoadIdentity()
// lighten things
ambient := []float32{0.5, 0.5, 0.5, 1}
diffuse := []float32{1, 1, 1, 1}
lightpos := []float32{-5, 5, 10, 0}
gl.Lightfv(gl.LIGHT0, gl.AMBIENT, ambient)
gl.Lightfv(gl.LIGHT0, gl.DIFFUSE, diffuse)
gl.Lightfv(gl.LIGHT0, gl.POSITION, lightpos)
gl.Enable(gl.LIGHT0)
gl.Disable(gl.TEXTURE_2D)
gl.Disable(gl.LIGHTING)
gl.Begin(gl.TRIANGLES)
gl.Color3f(1.0, 0.0, 0.0)
gl.Vertex3f(0, 0, 0.0)
gl.Color3f(0.0, 1.0, 0.0)
gl.Vertex3f(width, 0, 0.0)
gl.Color3f(0.0, 0.0, 1.0)
gl.Vertex3f(0.0, height, 0.0)
gl.End()
gl.Begin(gl.TRIANGLES)
gl.Color3f(1.0, 0.0, 0.0)
gl.Vertex3f(width, height, 0.0)
gl.Color3f(0.0, 0.0, 1.0)
gl.Vertex3f(0.0, height, 0.0)
gl.Color3f(0.0, 1.0, 0.0)
gl.Vertex3f(width, 0, 0.0)
gl.End()
gl.Enable(gl.TEXTURE_2D)
gl.Enable(gl.LIGHTING)
drawSprite(textures["sprites"], 0, 0, 0, lists["test"])
drawSprite(textures["sprites"], 320, 0, 0, lists["test"])
drawSprite(textures["sprites"], 640, 0, 0, lists["test"])
drawSprite(textures["sprites"], 320, 240, 0, lists["test"])
drawSprite(textures["sprites"], 320, 480, 0, lists["test"])
drawSprite(textures["sprites"], 0, 240, 0, lists["test"])
drawSprite(textures["sprites"], 0, 480, 0, lists["test"])
drawSprite(textures["sprites"], 640, 240, 0, lists["test"])
drawSprite(textures["sprites"], 640, 480, 0, lists["test"])
t := float64(time.Now().UnixNano()) / math.Pow(10, 9)
a := 2 * math.Pi * t / 60
x := (math.Sin(a) + 1) / 2 * float64(width)
y := (math.Cos(a) + 1) / 2 * float64(height)
drawSprite(textures["sprites"], x, y, -a, lists["test"])
a = 10 * 2 * math.Pi * t / 60
x = (math.Sin(a) + 1) / 2 * float64(width)
y = (math.Cos(a) + 1) / 2 * float64(height)
drawSprite(textures["sprites"], x, y, -a, lists["test"])
a = 60 * 2 * math.Pi * t / 60
x = (math.Sin(a) + 1) / 2 * float64(width)
y = (math.Cos(a) + 1) / 2 * float64(height)
drawSprite(textures["sprites"], x, y, -a, lists["test"])
if mouseVisible {
drawSprite(textures["sprites"], mouseX, mouseY, 0, lists["cursor"])
}
}