canvas/sdlcanvas/sdlcanvas.go
2018-03-28 11:30:46 +02:00

209 lines
4.9 KiB
Go

package sdlcanvas
import (
"fmt"
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"runtime"
"time"
"github.com/go-gl/gl/v3.2-core/gl"
"github.com/tfriedel6/canvas"
"github.com/tfriedel6/canvas/goglimpl"
"github.com/veandco/go-sdl2/sdl"
)
// Window represents the opened window with GL context. The Mouse* and Key*
// functions can be set for callbacks
type Window struct {
Window *sdl.Window
GLContext sdl.GLContext
frameTimes [10]time.Time
frameIndex int
frameCount int
fps float32
close bool
events []sdl.Event
Event func(event sdl.Event)
MouseDown func(button, x, y int)
MouseMove func(x, y int)
MouseUp func(button, x, y int)
KeyDown func(scancode int, rune rune, name string)
KeyUp func(scancode int, rune rune, name string)
}
// CreateWindow creates a window using SDL and initializes the OpenGL context
func CreateWindow(w, h int, title string) (*Window, *canvas.Canvas, error) {
runtime.LockOSThread()
// init SDL
err := sdl.Init(sdl.INIT_VIDEO)
if err != nil {
return nil, nil, fmt.Errorf("Error initializing SDL: %v", err)
}
sdl.GL_SetAttribute(sdl.GL_RED_SIZE, 8)
sdl.GL_SetAttribute(sdl.GL_GREEN_SIZE, 8)
sdl.GL_SetAttribute(sdl.GL_BLUE_SIZE, 8)
sdl.GL_SetAttribute(sdl.GL_ALPHA_SIZE, 8)
sdl.GL_SetAttribute(sdl.GL_DEPTH_SIZE, 0)
sdl.GL_SetAttribute(sdl.GL_STENCIL_SIZE, 8)
sdl.GL_SetAttribute(sdl.GL_DOUBLEBUFFER, 1)
sdl.GL_SetAttribute(sdl.GL_MULTISAMPLEBUFFERS, 1)
sdl.GL_SetAttribute(sdl.GL_MULTISAMPLESAMPLES, 4)
// create window
window, err := sdl.CreateWindow(title, sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED, w, h, sdl.WINDOW_RESIZABLE|sdl.WINDOW_OPENGL)
if err != nil {
return nil, nil, fmt.Errorf("Error creating window: %v", err)
}
// create GL context
glContext, err := sdl.GL_CreateContext(window)
if err != nil {
return nil, nil, fmt.Errorf("Error creating GL context: %v", err)
}
// init GL
err = gl.Init()
if err != nil {
return nil, nil, fmt.Errorf("Error initializing GL: %v", err)
}
sdl.GL_SetSwapInterval(1)
gl.Enable(gl.MULTISAMPLE)
err = canvas.LoadGL(goglimpl.GLImpl{})
if err != nil {
return nil, nil, fmt.Errorf("Error loading canvas GL assets: %v", err)
}
cv := canvas.New(0, 0, w, h)
wnd := &Window{
Window: window,
GLContext: glContext,
events: make([]sdl.Event, 0, 100),
}
return wnd, cv, nil
}
// Destroy destroys the GL context and the window
func (wnd *Window) Destroy() {
sdl.GL_DeleteContext(wnd.GLContext)
wnd.Window.Destroy()
}
// FPS returns the frames per second (averaged over 10 frames)
func (wnd *Window) FPS() float32 {
return wnd.fps
}
// Close can be used to end a call to MainLoop
func (wnd *Window) Close() {
wnd.close = true
}
// StartFrame handles events and gets the window ready for rendering
func (wnd *Window) StartFrame() error {
err := sdl.GL_MakeCurrent(wnd.Window, wnd.GLContext)
if err != nil {
return err
}
wnd.events = wnd.events[:0]
for {
event := sdl.PollEvent()
if event == nil {
break
}
handled := false
switch e := event.(type) {
case *sdl.MouseButtonEvent:
if e.Type == sdl.MOUSEBUTTONDOWN {
if wnd.MouseDown != nil {
wnd.MouseDown(int(e.Button), int(e.X), int(e.Y))
handled = true
}
} else if e.Type == sdl.MOUSEBUTTONUP {
if wnd.MouseUp != nil {
wnd.MouseUp(int(e.Button), int(e.X), int(e.Y))
handled = true
}
}
case *sdl.MouseMotionEvent:
if wnd.MouseMove != nil {
wnd.MouseMove(int(e.X), int(e.Y))
handled = true
}
case *sdl.KeyDownEvent:
if wnd.KeyDown != nil {
wnd.KeyDown(int(e.Keysym.Scancode), rune(e.Keysym.Unicode), keyName(e.Keysym.Scancode))
handled = true
}
case *sdl.KeyUpEvent:
if wnd.KeyUp != nil {
wnd.KeyUp(int(e.Keysym.Scancode), rune(e.Keysym.Unicode), keyName(e.Keysym.Scancode))
handled = true
}
}
if !handled && wnd.Event != nil {
wnd.Event(event)
handled = true
}
if !handled {
wnd.events = append(wnd.events, event)
}
}
return nil
}
// FinishFrame updates the FPS count and displays the frame
func (wnd *Window) FinishFrame() {
now := time.Now()
wnd.frameTimes[wnd.frameIndex] = now
wnd.frameIndex++
wnd.frameIndex %= len(wnd.frameTimes)
if wnd.frameCount < len(wnd.frameTimes) {
wnd.frameCount++
} else {
diff := now.Sub(wnd.frameTimes[wnd.frameIndex]).Seconds()
wnd.fps = float32(wnd.frameCount-1) / float32(diff)
}
sdl.GL_SwapWindow(wnd.Window)
}
// MainLoop runs a main loop and calls run on every frame
func (wnd *Window) MainLoop(run func()) {
// main loop
for !wnd.close {
err := wnd.StartFrame()
if err != nil {
time.Sleep(10 * time.Millisecond)
continue
}
for _, event := range wnd.events {
switch e := event.(type) {
case *sdl.WindowEvent:
if e.Event == sdl.WINDOWEVENT_CLOSE {
wnd.close = true
}
case *sdl.KeyDownEvent:
if e.Keysym.Scancode == sdl.SCANCODE_ESCAPE {
wnd.close = true
}
}
}
run()
wnd.FinishFrame()
}
}