lazarus/platform/window.go

232 lines
4.8 KiB
Go

package platform
import (
"errors"
"log"
"github.com/FooSoft/lazarus/graphics"
"github.com/FooSoft/lazarus/math"
"github.com/FooSoft/lazarus/platform/imgui"
"github.com/go-gl/gl/v2.1/gl"
"github.com/veandco/go-sdl2/sdl"
)
var (
ErrWindowExists = errors.New("only one window can exist at a time")
ErrWindowNotExists = errors.New("no window has been created")
)
type Scene interface {
Name() string
}
type SceneCreator interface {
Create() error
}
type SceneAdvancer interface {
Advance() error
}
type SceneDestroyer interface {
Destroy() error
}
var windowState struct {
sdlWindow *sdl.Window
sdlGlContext sdl.GLContext
scene Scene
}
func WindowCreate(title string, size math.Vec2i, scene Scene) error {
if WindowIsCreated() {
return ErrWindowExists
}
var err error
log.Println("window create")
if windowState.sdlWindow, err = sdl.CreateWindow(title, sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED, int32(size.X), int32(size.Y), sdl.WINDOW_OPENGL); err != nil {
return err
}
log.Println("window gl context create")
if windowState.sdlGlContext, err = windowState.sdlWindow.GLCreateContext(); err != nil {
WindowDestroy()
return err
}
log.Println("window gl context make current")
windowState.sdlWindow.GLMakeCurrent(windowState.sdlGlContext)
log.Println("window gl init")
if err := gl.Init(); err != nil {
WindowDestroy()
return err
}
if err := imgui.Create(); err != nil {
WindowDestroy()
return err
}
if err := WindowSetScene(scene); err != nil {
WindowDestroy()
return err
}
return nil
}
func WindowSetScene(scene Scene) error {
if !WindowIsCreated() {
return ErrWindowNotExists
}
if windowState.scene == scene {
return nil
}
sceneName := func(s Scene) string {
if s == nil {
return "<nil>"
} else {
return s.Name()
}
}
if sceneDestroyer, ok := windowState.scene.(SceneDestroyer); ok {
log.Printf("window scene notify destroy \"%s\"\n", sceneName(windowState.scene))
if err := sceneDestroyer.Destroy(); err != nil {
return err
}
}
log.Printf("window scene transition \"%v\" => \"%v\"\n", sceneName(windowState.scene), sceneName(scene))
windowState.scene = scene
if sceneCreator, ok := scene.(SceneCreator); ok {
log.Printf("window scene notify create \"%s\"\n", sceneName(windowState.scene))
if err := sceneCreator.Create(); err != nil {
return err
}
}
return nil
}
func WindowDestroy() error {
if !WindowIsCreated() {
return nil
}
if err := WindowSetScene(nil); err != nil {
return err
}
if err := imgui.Destroy(); err != nil {
return err
}
log.Println("window gl context destroy")
sdl.GLDeleteContext(windowState.sdlGlContext)
windowState.sdlGlContext = nil
log.Println("window destroy")
if err := windowState.sdlWindow.Destroy(); err != nil {
return err
}
windowState.sdlWindow = nil
return nil
}
func WindowRenderTexture(texture graphics.Texture, position math.Vec2i) error {
if !WindowIsCreated() {
return ErrWindowNotExists
}
size := texture.Size()
gl.Enable(gl.TEXTURE_2D)
gl.BindTexture(gl.TEXTURE_2D, uint32(texture.Id()))
gl.Begin(gl.QUADS)
gl.TexCoord2f(0, 0)
gl.Vertex2f(0, 0)
gl.TexCoord2f(0, 1)
gl.Vertex2f(0, float32(size.Y))
gl.TexCoord2f(1, 1)
gl.Vertex2f(float32(size.X), float32(size.Y))
gl.TexCoord2f(1, 0)
gl.Vertex2f(float32(size.X), 0)
gl.End()
return nil
}
func WindowDisplaySize() (math.Vec2i, error) {
if !WindowIsCreated() {
return math.Vec2i{}, ErrWindowNotExists
}
width, height := windowState.sdlWindow.GetSize()
return math.Vec2i{X: int(width), Y: int(height)}, nil
}
func WindowIsCreated() bool {
return windowState.sdlWindow != nil
}
func windowBufferSize() (math.Vec2i, error) {
if !WindowIsCreated() {
return math.Vec2i{}, ErrWindowNotExists
}
width, height := windowState.sdlWindow.GLGetDrawableSize()
return math.Vec2i{X: int(width), Y: int(height)}, nil
}
func windowAdvance() (bool, error) {
if !WindowIsCreated() {
return false, ErrWindowNotExists
}
displaySize, err := WindowDisplaySize()
if err != nil {
return false, err
}
bufferSize, err := windowBufferSize()
if err != nil {
return false, err
}
imgui.BeginFrame(displaySize, bufferSize)
gl.Viewport(0, 0, int32(displaySize.X), int32(displaySize.Y))
gl.Clear(gl.COLOR_BUFFER_BIT)
gl.MatrixMode(gl.PROJECTION)
gl.LoadIdentity()
gl.Ortho(0, float64(displaySize.X), float64(displaySize.Y), 0, -1, 1)
gl.MatrixMode(gl.MODELVIEW)
gl.LoadIdentity()
if sceneAdvancer, ok := windowState.scene.(SceneAdvancer); ok {
if err := sceneAdvancer.Advance(); err != nil {
return false, err
}
}
imgui.EndFrame()
windowState.sdlWindow.GLSwap()
return windowState.scene != nil, nil
}
func windowProcessEvent(event sdl.Event) (bool, error) {
if !WindowIsCreated() {
return false, ErrWindowNotExists
}
return imgui.ProcessEvent(event)
}