work on getting platform to abstract updating and rendering

add "Scene" interface to provide customization point for clients
This commit is contained in:
Alex Yatskov 2018-12-28 09:26:05 -08:00
parent 5d95d2ef77
commit 7c024b537e
3 changed files with 84 additions and 36 deletions

View File

@ -15,7 +15,7 @@ var (
ErrWasNotInit = errors.New("imgui backend was not initialized") ErrWasNotInit = errors.New("imgui backend was not initialized")
) )
var state struct { var singleton struct {
isInit bool isInit bool
buttonsDown [3]bool buttonsDown [3]bool
lastTime uint64 lastTime uint64
@ -24,11 +24,11 @@ var state struct {
} }
func Init() error { func Init() error {
if state.isInit { if singleton.isInit {
return ErrAlreadyInit return ErrAlreadyInit
} }
state.context = imgui.CreateContext(nil) singleton.context = imgui.CreateContext(nil)
keys := map[int]int{ keys := map[int]int{
imgui.KeyTab: sdl.SCANCODE_TAB, imgui.KeyTab: sdl.SCANCODE_TAB,
@ -60,30 +60,30 @@ func Init() error {
io.KeyMap(imguiKey, nativeKey) io.KeyMap(imguiKey, nativeKey)
} }
state.fontTexture = createFontTexture() singleton.fontTexture = createFontTexture()
state.isInit = true singleton.isInit = true
return nil return nil
} }
func Shutdown() error { func Shutdown() error {
if !state.isInit { if !singleton.isInit {
return ErrWasNotInit return ErrWasNotInit
} }
state.isInit = false singleton.isInit = false
destroyFontTexture(state.fontTexture) destroyFontTexture(singleton.fontTexture)
state.fontTexture = 0 singleton.fontTexture = 0
state.context.Destroy() singleton.context.Destroy()
state.context = nil singleton.context = nil
return nil return nil
} }
func NewFrame(windowSize math.Vec2i) error { func NewFrame(windowSize math.Vec2i) error {
if !state.isInit { if !singleton.isInit {
return ErrWasNotInit return ErrWasNotInit
} }
@ -94,18 +94,18 @@ func NewFrame(windowSize math.Vec2i) error {
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution) // Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
frequency := sdl.GetPerformanceFrequency() frequency := sdl.GetPerformanceFrequency()
currentTime := sdl.GetPerformanceCounter() currentTime := sdl.GetPerformanceCounter()
if state.lastTime > 0 { if singleton.lastTime > 0 {
io.SetDeltaTime(float32(currentTime-state.lastTime) / float32(frequency)) io.SetDeltaTime(float32(currentTime-singleton.lastTime) / float32(frequency))
} else { } else {
io.SetDeltaTime(1.0 / 60.0) io.SetDeltaTime(1.0 / 60.0)
} }
state.lastTime = currentTime singleton.lastTime = currentTime
// If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame.
x, y, state := sdl.GetMouseState() x, y, state := sdl.GetMouseState()
for i, button := range []uint32{sdl.BUTTON_LEFT, sdl.BUTTON_RIGHT, sdl.BUTTON_MIDDLE} { for i, button := range []uint32{sdl.BUTTON_LEFT, sdl.BUTTON_RIGHT, sdl.BUTTON_MIDDLE} {
io.SetMouseButtonDown(i, state.buttonsDown[i] || (state&sdl.Button(button)) != 0) io.SetMouseButtonDown(i, singleton.buttonsDown[i] || (state&sdl.Button(button)) != 0)
state.buttonsDown[i] = false singleton.buttonsDown[i] = false
} }
io.SetMousePosition(imgui.Vec2{X: float32(x), Y: float32(y)}) io.SetMousePosition(imgui.Vec2{X: float32(x), Y: float32(y)})
@ -120,7 +120,7 @@ func NewFrame(windowSize math.Vec2i) error {
// Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags.
// If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field. // If you have multiple SDL events and some of them are not meant to be used by dear imgui, you may need to filter events based on their windowID field.
func ProcessEvent(event sdl.Event) (bool, error) { func ProcessEvent(event sdl.Event) (bool, error) {
if !state.isInit { if !singleton.isInit {
return false, ErrWasNotInit return false, ErrWasNotInit
} }
@ -143,13 +143,13 @@ func ProcessEvent(event sdl.Event) (bool, error) {
buttonEvent := event.(*sdl.MouseButtonEvent) buttonEvent := event.(*sdl.MouseButtonEvent)
switch buttonEvent.Button { switch buttonEvent.Button {
case sdl.BUTTON_LEFT: case sdl.BUTTON_LEFT:
state.buttonsDown[0] = true singleton.buttonsDown[0] = true
break break
case sdl.BUTTON_RIGHT: case sdl.BUTTON_RIGHT:
state.buttonsDown[1] = true singleton.buttonsDown[1] = true
break break
case sdl.BUTTON_MIDDLE: case sdl.BUTTON_MIDDLE:
state.buttonsDown[2] = true singleton.buttonsDown[2] = true
break break
} }
return true, nil return true, nil
@ -178,9 +178,9 @@ func ProcessEvent(event sdl.Event) (bool, error) {
} }
// OpenGL2 Render function. // OpenGL2 Render function.
// Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL state explicitly, in order to be able to run within any OpenGL engine that doesn't do so. // Note that this implementation is little overcomplicated because we are saving/setting up/restoring every OpenGL singleton explicitly, in order to be able to run within any OpenGL engine that doesn't do so.
func Render(windowSize, fbSize math.Vec2i, drawData imgui.DrawData) error { func Render(windowSize, fbSize math.Vec2i, drawData imgui.DrawData) error {
if !state.isInit { if !singleton.isInit {
return ErrWasNotInit return ErrWasNotInit
} }
@ -190,7 +190,7 @@ func Render(windowSize, fbSize math.Vec2i, drawData imgui.DrawData) error {
}) })
// We are using the OpenGL fixed pipeline to make the example code simpler to read! // We are using the OpenGL fixed pipeline to make the example code simpler to read!
// Setup render state: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill. // Setup render singleton: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill.
var lastTexture int32 var lastTexture int32
gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture) gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
var lastPolygonMode [2]int32 var lastPolygonMode [2]int32
@ -296,7 +296,7 @@ func createFontTexture() uint32 {
// Store our identifier // Store our identifier
io.Fonts().SetTextureID(imgui.TextureID(fontTexture)) io.Fonts().SetTextureID(imgui.TextureID(fontTexture))
// Restore state // Restore singleton
gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture)) gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture))
return fontTexture return fontTexture
} }

View File

@ -17,7 +17,11 @@ var (
var state struct { var state struct {
isInit bool isInit bool
windows []Window windows []*window
}
type Scene interface {
Advance()
} }
func Init() error { func Init() error {
@ -69,12 +73,13 @@ func ProcessEvents() error {
return ErrWasNotInit return ErrWasNotInit
} }
var terminate bool for running := true; running; {
for !terminate { advanceWindows()
renderWindows()
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() { for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch event.(type) { if !handleEvent(event) {
case *sdl.QuitEvent: running = false
terminate = true
break break
} }
} }
@ -85,12 +90,12 @@ func ProcessEvents() error {
return nil return nil
} }
func CreateWindow(title string, width, height int) (Window, error) { func CreateWindow(title string, width, height int, scene Scene) (Window, error) {
if !state.isInit { if !state.isInit {
return nil, ErrWasNotInit return nil, ErrWasNotInit
} }
window, err := newWindow(title, width, height) window, err := newWindow(title, width, height, scene)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -99,3 +104,24 @@ func CreateWindow(title string, width, height int) (Window, error) {
return window, err return window, err
} }
func advanceWindows() {
for _, window := range state.windows {
window.advance()
}
}
func renderWindows() {
for _, window := range state.windows {
window.render()
}
}
func handleEvent(event sdl.Event) bool {
switch event.(type) {
case *sdl.QuitEvent:
return false
}
return true
}

View File

@ -1,6 +1,9 @@
package platform package platform
import "github.com/veandco/go-sdl2/sdl" import (
"github.com/FooSoft/lazarus/math"
"github.com/veandco/go-sdl2/sdl"
)
type Window interface { type Window interface {
Destroy() error Destroy() error
@ -10,9 +13,10 @@ type window struct {
sdlWindow *sdl.Window sdlWindow *sdl.Window
sdlGlContext sdl.GLContext sdlGlContext sdl.GLContext
sdlRenderer *sdl.Renderer sdlRenderer *sdl.Renderer
scene Scene
} }
func newWindow(title string, width, height int) (Window, error) { func newWindow(title string, width, height int, scene Scene) (*window, error) {
sdlWindow, err := sdl.CreateWindow( sdlWindow, err := sdl.CreateWindow(
title, title,
sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED,
@ -37,7 +41,7 @@ func newWindow(title string, width, height int) (Window, error) {
return nil, err return nil, err
} }
return &window{sdlWindow, sdlGlContext, sdlRenderer}, nil return &window{sdlWindow, sdlGlContext, sdlRenderer, scene}, nil
} }
func (w *window) Destroy() error { func (w *window) Destroy() error {
@ -52,3 +56,21 @@ func (w *window) Destroy() error {
return nil return nil
} }
func (w *window) advance() {
w.scene.Advance()
}
func (w *window) render() {
w.sdlWindow.GLMakeCurrent(w.sdlGlContext)
}
func (w *window) displaySize() math.Vec2i {
width, height := w.sdlWindow.GetSize()
return math.Vec2i{X: int(width), Y: int(height)}
}
func (w *window) bufferSize() math.Vec2i {
width, height := w.sdlWindow.GLGetDrawableSize()
return math.Vec2i{X: int(width), Y: int(height)}
}