make Window and ImgUi into singletons; there will only ever be one of each

This commit is contained in:
Alex Yatskov 2019-01-06 17:00:19 -08:00
parent 078f97ae87
commit 6faac2f08e
7 changed files with 248 additions and 280 deletions

View File

@ -1,9 +1,9 @@
package imgui package imgui
// #cgo linux LDFLAGS: -L./cimgui -l:cimgui.a -lstdc++ -lm
// #include "native.h" // #include "native.h"
import "C" import "C"
import ( import (
"errors"
"log" "log"
"unsafe" "unsafe"
@ -12,6 +12,10 @@ import (
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
) )
var (
ErrNotInit = errors.New("imgui context was not created")
)
const ( const (
pointerSize = unsafe.Sizeof(C.uintptr_t(0)) pointerSize = unsafe.Sizeof(C.uintptr_t(0))
drawCommandSize = unsafe.Sizeof(C.ImDrawCmd{}) drawCommandSize = unsafe.Sizeof(C.ImDrawCmd{})
@ -46,159 +50,162 @@ var keyMapping = map[int]C.int{
C.ImGuiKey_Z: sdl.SCANCODE_Z, C.ImGuiKey_Z: sdl.SCANCODE_Z,
} }
var singleton struct { var imguiState struct {
nativeContext *C.ImGuiContext imguiContext *C.ImGuiContext
nativeIo *C.ImGuiIO imguiIo *C.ImGuiIO
refCount int
}
type Context struct {
buttonsDown [3]bool
lastTime uint64
fontTexture uint32 fontTexture uint32
displaySize math.Vec2i displaySize math.Vec2i
bufferSize math.Vec2i bufferSize math.Vec2i
buttonsDown [3]bool
lastTime uint64
} }
func New(displaySize, bufferSize math.Vec2i) (*Context, error) { func Create() error {
singleton.refCount++ if IsCreated() {
if singleton.refCount == 1 { return nil
log.Println("imgui global create")
singleton.nativeContext = C.igCreateContext(nil)
singleton.nativeIo = C.igGetIO()
for imguiKey, nativeKey := range keyMapping {
singleton.nativeIo.KeyMap[imguiKey] = nativeKey
}
} }
log.Println("imgui context create") log.Println("imgui create")
c := &Context{displaySize: displaySize, bufferSize: bufferSize} imguiState.imguiContext = C.igCreateContext(nil)
imguiState.imguiIo = C.igGetIO()
for imguiKey, nativeKey := range keyMapping {
imguiState.imguiIo.KeyMap[imguiKey] = nativeKey
}
var imageData *C.uchar var imageData *C.uchar
var imageWidth, imageHeight C.int var imageWidth, imageHeight C.int
C.ImFontAtlas_GetTexDataAsRGBA32(singleton.nativeIo.Fonts, &imageData, &imageWidth, &imageHeight, nil) C.ImFontAtlas_GetTexDataAsRGBA32(imguiState.imguiIo.Fonts, &imageData, &imageWidth, &imageHeight, nil)
var lastTexture int32 var lastTexture int32
gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture) gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
gl.GenTextures(1, &c.fontTexture) gl.GenTextures(1, &imguiState.fontTexture)
gl.BindTexture(gl.TEXTURE_2D, c.fontTexture) gl.BindTexture(gl.TEXTURE_2D, imguiState.fontTexture)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0) gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0)
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(imageWidth), int32(imageHeight), 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(imageData)) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(imageWidth), int32(imageHeight), 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(imageData))
gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture)) gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture))
return c, nil return nil
} }
func (c *Context) Destroy() error { func IsCreated() bool {
if c == nil || c.fontTexture == 0 { return imguiState.imguiContext != nil
}
func Destroy() error {
if !IsCreated() {
return nil return nil
} }
log.Println("imgui context destroy") gl.DeleteTextures(1, &imguiState.fontTexture)
gl.DeleteTextures(1, &c.fontTexture) imguiState.imguiIo.Fonts.TexID = C.nativeHandleCast(C.uintptr_t(imguiState.fontTexture))
singleton.nativeIo.Fonts.TexID = C.nativeHandleCast(C.uintptr_t(c.fontTexture)) imguiState.fontTexture = 0
c.fontTexture = 0
singleton.refCount-- log.Println("imgui destroy")
if singleton.refCount == 0 { C.igDestroyContext(imguiState.imguiContext)
log.Println("imgui global destroy") imguiState.imguiContext = nil
C.igDestroyContext(singleton.nativeContext) imguiState.imguiIo = nil
singleton.nativeContext = nil
singleton.nativeIo = nil
}
return nil return nil
} }
func (c *Context) SetDisplaySize(displaySize math.Vec2i) { func BeginFrame(displaySize, bufferSize math.Vec2i) error {
c.displaySize = displaySize if !IsCreated() {
} return ErrNotInit
}
func (c *Context) SetBufferSize(bufferSize math.Vec2i) { imguiState.displaySize = displaySize
c.bufferSize = bufferSize imguiState.bufferSize = bufferSize
}
func (c *Context) BeginFrame() { imguiState.imguiIo.Fonts.TexID = C.nativeHandleCast(C.uintptr_t(imguiState.fontTexture))
singleton.nativeIo.Fonts.TexID = C.nativeHandleCast(C.uintptr_t(c.fontTexture)) imguiState.imguiIo.DisplaySize.x = C.float(displaySize.X)
singleton.nativeIo.DisplaySize.x = C.float(c.displaySize.X) imguiState.imguiIo.DisplaySize.y = C.float(displaySize.Y)
singleton.nativeIo.DisplaySize.y = C.float(c.displaySize.Y)
currentTime := sdl.GetPerformanceCounter() currentTime := sdl.GetPerformanceCounter()
if c.lastTime > 0 { if imguiState.lastTime > 0 {
singleton.nativeIo.DeltaTime = C.float(float32(currentTime-c.lastTime) / float32(sdl.GetPerformanceFrequency())) imguiState.imguiIo.DeltaTime = C.float(float32(currentTime-imguiState.lastTime) / float32(sdl.GetPerformanceFrequency()))
} else { } else {
singleton.nativeIo.DeltaTime = C.float(1.0 / 60.0) imguiState.imguiIo.DeltaTime = C.float(1.0 / 60.0)
} }
c.lastTime = currentTime imguiState.lastTime = currentTime
x, y, state := sdl.GetMouseState() x, y, state := sdl.GetMouseState()
singleton.nativeIo.MousePos.x = C.float(x) imguiState.imguiIo.MousePos.x = C.float(x)
singleton.nativeIo.MousePos.y = C.float(y) imguiState.imguiIo.MousePos.y = C.float(y)
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} {
singleton.nativeIo.MouseDown[i] = C.bool(c.buttonsDown[i] || (state&sdl.Button(button)) != 0) imguiState.imguiIo.MouseDown[i] = C.bool(imguiState.buttonsDown[i] || (state&sdl.Button(button)) != 0)
c.buttonsDown[i] = false imguiState.buttonsDown[i] = false
} }
C.igNewFrame() C.igNewFrame()
return nil
} }
func (c *Context) ProcessEvent(event sdl.Event) (bool, error) { func ProcessEvent(event sdl.Event) (bool, error) {
if !IsCreated() {
return false, ErrNotInit
}
switch event.GetType() { switch event.GetType() {
case sdl.MOUSEWHEEL: case sdl.MOUSEWHEEL:
wheelEvent := event.(*sdl.MouseWheelEvent) wheelEvent := event.(*sdl.MouseWheelEvent)
if wheelEvent.X > 0 { if wheelEvent.X > 0 {
singleton.nativeIo.MouseDelta.x++ imguiState.imguiIo.MouseDelta.x++
} else if wheelEvent.X < 0 { } else if wheelEvent.X < 0 {
singleton.nativeIo.MouseDelta.x-- imguiState.imguiIo.MouseDelta.x--
} }
if wheelEvent.Y > 0 { if wheelEvent.Y > 0 {
singleton.nativeIo.MouseDelta.y++ imguiState.imguiIo.MouseDelta.y++
} else if wheelEvent.Y < 0 { } else if wheelEvent.Y < 0 {
singleton.nativeIo.MouseDelta.y-- imguiState.imguiIo.MouseDelta.y--
} }
return true, nil return true, nil
case sdl.MOUSEBUTTONDOWN: case sdl.MOUSEBUTTONDOWN:
buttonEvent := event.(*sdl.MouseButtonEvent) buttonEvent := event.(*sdl.MouseButtonEvent)
switch buttonEvent.Button { switch buttonEvent.Button {
case sdl.BUTTON_LEFT: case sdl.BUTTON_LEFT:
c.buttonsDown[0] = true imguiState.buttonsDown[0] = true
break break
case sdl.BUTTON_RIGHT: case sdl.BUTTON_RIGHT:
c.buttonsDown[1] = true imguiState.buttonsDown[1] = true
break break
case sdl.BUTTON_MIDDLE: case sdl.BUTTON_MIDDLE:
c.buttonsDown[2] = true imguiState.buttonsDown[2] = true
break break
} }
return true, nil return true, nil
case sdl.TEXTINPUT: case sdl.TEXTINPUT:
inputEvent := event.(*sdl.TextInputEvent) inputEvent := event.(*sdl.TextInputEvent)
C.ImGuiIO_AddInputCharactersUTF8(singleton.nativeIo, (*C.char)(unsafe.Pointer(&inputEvent.Text[0]))) C.ImGuiIO_AddInputCharactersUTF8(imguiState.imguiIo, (*C.char)(unsafe.Pointer(&inputEvent.Text[0])))
return true, nil return true, nil
case sdl.KEYDOWN: case sdl.KEYDOWN:
keyEvent := event.(*sdl.KeyboardEvent) keyEvent := event.(*sdl.KeyboardEvent)
singleton.nativeIo.KeysDown[keyEvent.Keysym.Scancode] = true imguiState.imguiIo.KeysDown[keyEvent.Keysym.Scancode] = true
modState := sdl.GetModState() modState := sdl.GetModState()
singleton.nativeIo.KeyCtrl = C.bool(modState&sdl.KMOD_CTRL != 0) imguiState.imguiIo.KeyCtrl = C.bool(modState&sdl.KMOD_CTRL != 0)
singleton.nativeIo.KeyAlt = C.bool(modState&sdl.KMOD_ALT != 0) imguiState.imguiIo.KeyAlt = C.bool(modState&sdl.KMOD_ALT != 0)
singleton.nativeIo.KeyShift = C.bool(modState&sdl.KMOD_SHIFT != 0) imguiState.imguiIo.KeyShift = C.bool(modState&sdl.KMOD_SHIFT != 0)
case sdl.KEYUP: case sdl.KEYUP:
keyEvent := event.(*sdl.KeyboardEvent) keyEvent := event.(*sdl.KeyboardEvent)
singleton.nativeIo.KeysDown[keyEvent.Keysym.Scancode] = false imguiState.imguiIo.KeysDown[keyEvent.Keysym.Scancode] = false
modState := sdl.GetModState() modState := sdl.GetModState()
singleton.nativeIo.KeyCtrl = C.bool(modState&sdl.KMOD_CTRL != 0) imguiState.imguiIo.KeyCtrl = C.bool(modState&sdl.KMOD_CTRL != 0)
singleton.nativeIo.KeyAlt = C.bool(modState&sdl.KMOD_ALT != 0) imguiState.imguiIo.KeyAlt = C.bool(modState&sdl.KMOD_ALT != 0)
singleton.nativeIo.KeyShift = C.bool(modState&sdl.KMOD_SHIFT != 0) imguiState.imguiIo.KeyShift = C.bool(modState&sdl.KMOD_SHIFT != 0)
return true, nil return true, nil
} }
return false, nil return false, nil
} }
func (c *Context) EndFrame() error { func EndFrame() error {
if !IsCreated() {
return ErrNotInit
}
C.igRender() C.igRender()
var lastTexture int32 var lastTexture int32
@ -223,11 +230,11 @@ func (c *Context) EndFrame() error {
gl.Enable(gl.TEXTURE_2D) gl.Enable(gl.TEXTURE_2D)
gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL) gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL)
gl.Viewport(0, 0, int32(c.bufferSize.X), int32(c.bufferSize.Y)) gl.Viewport(0, 0, int32(imguiState.bufferSize.X), int32(imguiState.bufferSize.Y))
gl.MatrixMode(gl.PROJECTION) gl.MatrixMode(gl.PROJECTION)
gl.PushMatrix() gl.PushMatrix()
gl.LoadIdentity() gl.LoadIdentity()
gl.Ortho(0, float64(c.displaySize.X), float64(c.displaySize.Y), 0, -1, 1) gl.Ortho(0, float64(imguiState.displaySize.X), float64(imguiState.displaySize.Y), 0, -1, 1)
gl.MatrixMode(gl.MODELVIEW) gl.MatrixMode(gl.MODELVIEW)
gl.PushMatrix() gl.PushMatrix()
gl.LoadIdentity() gl.LoadIdentity()
@ -236,8 +243,8 @@ func (c *Context) EndFrame() error {
C.ImDrawData_ScaleClipRects( C.ImDrawData_ScaleClipRects(
drawData, drawData,
C.ImVec2{ C.ImVec2{
x: C.float(float32(c.bufferSize.X) / float32(c.displaySize.X)), x: C.float(float32(imguiState.bufferSize.X) / float32(imguiState.displaySize.X)),
y: C.float(float32(c.bufferSize.Y) / float32(c.displaySize.Y)), y: C.float(float32(imguiState.bufferSize.Y) / float32(imguiState.displaySize.Y)),
}, },
) )
@ -257,7 +264,7 @@ func (c *Context) EndFrame() error {
command := (*C.ImDrawCmd)(unsafe.Pointer(uintptr(unsafe.Pointer(commandList.CmdBuffer.Data)) + drawCommandSize*uintptr(j))) command := (*C.ImDrawCmd)(unsafe.Pointer(uintptr(unsafe.Pointer(commandList.CmdBuffer.Data)) + drawCommandSize*uintptr(j)))
gl.Scissor( gl.Scissor(
int32(command.ClipRect.x), int32(command.ClipRect.x),
int32(c.bufferSize.Y)-int32(command.ClipRect.w), int32(imguiState.bufferSize.Y)-int32(command.ClipRect.w),
int32(command.ClipRect.z-command.ClipRect.x), int32(command.ClipRect.z-command.ClipRect.x),
int32(command.ClipRect.w-command.ClipRect.y), int32(command.ClipRect.w-command.ClipRect.y),
) )

View File

@ -1,5 +1,6 @@
package imgui package imgui
// #cgo linux LDFLAGS: -L./cimgui -l:cimgui.a -lstdc++ -lm
// #include "native.h" // #include "native.h"
import "C" import "C"
import ( import (
@ -9,27 +10,27 @@ import (
"github.com/FooSoft/lazarus/math" "github.com/FooSoft/lazarus/math"
) )
func (*Context) DialogBegin(label string) bool { func DialogBegin(label string) bool {
labelC := C.CString(label) labelC := C.CString(label)
defer C.free(unsafe.Pointer(labelC)) defer C.free(unsafe.Pointer(labelC))
return bool(C.igBegin(labelC, nil, 0)) return bool(C.igBegin(labelC, nil, 0))
} }
func (*Context) DialogEnd() { func DialogEnd() {
C.igEnd() C.igEnd()
} }
func (*Context) Button(label string) bool { func Button(label string) bool {
labelC := C.CString(label) labelC := C.CString(label)
defer C.free(unsafe.Pointer(labelC)) defer C.free(unsafe.Pointer(labelC))
return bool(C.igButton(labelC, C.ImVec2{})) return bool(C.igButton(labelC, C.ImVec2{}))
} }
func (c *Context) Image(texture graphics.Texture) { func Image(texture graphics.Texture) {
c.ImageSized(texture, texture.Size()) ImageSized(texture, texture.Size())
} }
func (*Context) ImageSized(texture graphics.Texture, size math.Vec2i) { func ImageSized(texture graphics.Texture, size math.Vec2i) {
C.igImage( C.igImage(
C.nativeHandleCast(C.uintptr_t(texture.Id())), C.nativeHandleCast(C.uintptr_t(texture.Id())),
C.ImVec2{x: C.float(size.X), y: C.float(size.Y)}, C.ImVec2{x: C.float(size.X), y: C.float(size.Y)},
@ -40,7 +41,7 @@ func (*Context) ImageSized(texture graphics.Texture, size math.Vec2i) {
) )
} }
func (*Context) SliderInt(label string, value *int, min, max int) bool { func SliderInt(label string, value *int, min, max int) bool {
labelC := C.CString(label) labelC := C.CString(label)
defer C.free(unsafe.Pointer(labelC)) defer C.free(unsafe.Pointer(labelC))
valueC := C.int(*value) valueC := C.int(*value)
@ -49,7 +50,7 @@ func (*Context) SliderInt(label string, value *int, min, max int) bool {
return result return result
} }
func (*Context) Text(label string) { func Text(label string) {
labelStartC := C.CString(label) labelStartC := C.CString(label)
labelEndC := (*C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(labelStartC)) + uintptr(len(label)))) labelEndC := (*C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(labelStartC)) + uintptr(len(label))))
defer C.free(unsafe.Pointer(labelStartC)) defer C.free(unsafe.Pointer(labelStartC))

View File

@ -4,99 +4,45 @@ import (
"log" "log"
"runtime" "runtime"
"github.com/FooSoft/lazarus/math"
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
) )
var singleton struct { var platfromState struct {
sdlIsInit bool sdlIsInit bool
windows []*Window
} }
func Advance() (bool, error) { func Advance() (bool, error) {
if err := advanceWindows(); err != nil {
return false, err
}
for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() { for event := sdl.PollEvent(); event != nil; event = sdl.PollEvent() {
switch event.(type) { switch event.(type) {
case *sdl.QuitEvent: case *sdl.QuitEvent:
return false, nil return false, nil
default: default:
if err := processWindowEvents(event); err != nil { if _, err := windowProcessEvent(event); err != nil {
return false, err return false, err
} }
} }
} }
return len(singleton.windows) > 0, nil run, err := windowAdvance()
if !run {
WindowDestroy()
}
return run, err
} }
func NewWindow(title string, size math.Vec2i, scene Scene) (*Window, error) { func platformInit() error {
if !singleton.sdlIsInit { if platfromState.sdlIsInit {
runtime.LockOSThread() return nil
log.Println("sdl global init")
if err := sdl.Init(sdl.INIT_VIDEO); err != nil {
return nil, err
}
sdl.GLSetAttribute(sdl.GL_CONTEXT_MAJOR_VERSION, 2)
sdl.GLSetAttribute(sdl.GL_CONTEXT_MINOR_VERSION, 1)
sdl.GLSetAttribute(sdl.GL_DOUBLEBUFFER, 1)
singleton.sdlIsInit = true
} }
w, err := newWindow(title, size, scene) runtime.LockOSThread()
if err != nil {
return nil, err log.Println("sdl init")
} if err := sdl.Init(sdl.INIT_VIDEO); err != nil {
return err
appendWindow(w)
return w, err
}
func appendWindow(window *Window) {
singleton.windows = append(singleton.windows, window)
}
func removeWindow(window *Window) bool {
for i, w := range singleton.windows {
if w == window {
singleton.windows = append(singleton.windows[:i], singleton.windows[i+1:]...)
return true
}
}
return false
}
func advanceWindows() error {
var windowsToRemove []*Window
for _, window := range singleton.windows {
run, err := window.advance()
if err != nil {
return err
}
if !run {
windowsToRemove = append(windowsToRemove, window)
}
}
for _, window := range windowsToRemove {
removeWindow(window)
}
return nil
}
func processWindowEvents(event sdl.Event) error {
for _, window := range singleton.windows {
if _, err := window.processEvent(event); err != nil {
return err
}
} }
platfromState.sdlIsInit = true
return nil return nil
} }

View File

@ -5,21 +5,13 @@ type Scene interface {
} }
type SceneCreator interface { type SceneCreator interface {
Create(window *Window) error Create() error
} }
type SceneAdvancer interface { type SceneAdvancer interface {
Advance(window *Window) error Advance() error
} }
type SceneDestroyer interface { type SceneDestroyer interface {
Destroy(window *Window) error Destroy() error
}
func sceneName(scene Scene) string {
if scene == nil {
return "<nil>"
} else {
return scene.Name()
}
} }

View File

@ -9,11 +9,15 @@ import (
) )
type texture struct { type texture struct {
size math.Vec2i
glTexture uint32 glTexture uint32
size math.Vec2i
} }
func newTextureFromRgba(colors []math.Color4b, size math.Vec2i) (graphics.Texture, error) { func NewTextureFromRgba(colors []math.Color4b, size math.Vec2i) (graphics.Texture, error) {
if !WindowIsCreated() {
return nil, ErrWindowNotExists
}
var glLastTexture int32 var glLastTexture int32
gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &glLastTexture) gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &glLastTexture)
@ -26,10 +30,14 @@ func newTextureFromRgba(colors []math.Color4b, size math.Vec2i) (graphics.Textur
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(size.X), int32(size.Y), 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(&colors[0])) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(size.X), int32(size.Y), 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(&colors[0]))
gl.BindTexture(gl.TEXTURE_2D, uint32(glLastTexture)) gl.BindTexture(gl.TEXTURE_2D, uint32(glLastTexture))
return &texture{size, glTexture}, nil return &texture{glTexture, size}, nil
} }
func newTextureFromRgb(colors []math.Color3b, size math.Vec2i) (graphics.Texture, error) { func NewTextureFromRgb(colors []math.Color3b, size math.Vec2i) (graphics.Texture, error) {
if !WindowIsCreated() {
return nil, ErrWindowNotExists
}
var glLastTexture int32 var glLastTexture int32
gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &glLastTexture) gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &glLastTexture)
@ -43,7 +51,7 @@ func newTextureFromRgb(colors []math.Color3b, size math.Vec2i) (graphics.Texture
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, int32(size.X), int32(size.Y), 0, gl.RGB, gl.UNSIGNED_BYTE, unsafe.Pointer(&colors[0])) gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGB, int32(size.X), int32(size.Y), 0, gl.RGB, gl.UNSIGNED_BYTE, unsafe.Pointer(&colors[0]))
gl.BindTexture(gl.TEXTURE_2D, uint32(glLastTexture)) gl.BindTexture(gl.TEXTURE_2D, uint32(glLastTexture))
return &texture{size, glTexture}, nil return &texture{glTexture, size}, nil
} }
func (t *texture) Id() graphics.TextureId { func (t *texture) Id() graphics.TextureId {

View File

@ -1,6 +1,7 @@
package platform package platform
import ( import (
"errors"
"log" "log"
"github.com/FooSoft/lazarus/graphics" "github.com/FooSoft/lazarus/graphics"
@ -10,78 +11,88 @@ import (
"github.com/veandco/go-sdl2/sdl" "github.com/veandco/go-sdl2/sdl"
) )
type Window struct { var (
ErrWindowExists = errors.New("only one window can exist at a time")
ErrWindowNotExists = errors.New("no window has been created")
)
var windowState struct {
sdlWindow *sdl.Window sdlWindow *sdl.Window
sdlGlContext sdl.GLContext sdlGlContext sdl.GLContext
imguiContext *imgui.Context
scene Scene scene Scene
} }
func newWindow(title string, size math.Vec2i, scene Scene) (*Window, error) { func WindowCreate(title string, size math.Vec2i, scene Scene) error {
if WindowIsCreated() {
return ErrWindowExists
}
platformInit()
var err error
log.Println("window create") log.Println("window create")
sdlWindow, err := sdl.CreateWindow( if windowState.sdlWindow, err = sdl.CreateWindow(title, sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED, int32(size.X), int32(size.Y), sdl.WINDOW_OPENGL); err != nil {
title, return err
sdl.WINDOWPOS_CENTERED,
sdl.WINDOWPOS_CENTERED,
int32(size.X),
int32(size.Y),
sdl.WINDOW_OPENGL,
)
if err != nil {
return nil, err
} }
sdlGlContext, err := sdlWindow.GLCreateContext() log.Println("window gl context create")
if err != nil { if windowState.sdlGlContext, err = windowState.sdlWindow.GLCreateContext(); err != nil {
sdlWindow.Destroy() WindowDestroy()
return nil, err return err
} }
w := &Window{ log.Println("window gl context make current")
sdlWindow: sdlWindow, windowState.sdlWindow.GLMakeCurrent(windowState.sdlGlContext)
sdlGlContext: sdlGlContext,
}
w.makeCurrent() log.Println("window gl init")
log.Println("opengl init")
if err := gl.Init(); err != nil { if err := gl.Init(); err != nil {
return nil, err WindowDestroy()
return err
} }
w.imguiContext, err = imgui.New(w.DisplaySize(), w.BufferSize()) if err := imgui.Create(); err != nil {
if err != nil { WindowDestroy()
w.Destroy() return err
return nil, err
} }
if err := w.SetScene(scene); err != nil { if err := WindowSetScene(scene); err != nil {
w.Destroy() WindowDestroy()
return nil, err return err
} }
return w, nil return nil
} }
func (w *Window) SetScene(scene Scene) error { func WindowSetScene(scene Scene) error {
if w.scene == scene { if !WindowIsCreated() {
return ErrWindowNotExists
}
if windowState.scene == scene {
return nil return nil
} }
log.Printf("scene transition \"%v\" => \"%v\"\n", sceneName(w.scene), sceneName(scene)) sceneName := func(s Scene) string {
if s == nil {
return "<nil>"
} else {
return s.Name()
}
}
if sceneDestroyer, ok := w.scene.(SceneDestroyer); ok { if sceneDestroyer, ok := windowState.scene.(SceneDestroyer); ok {
log.Printf("scene notify destroy \"%s\"\n", sceneName(w.scene)) log.Printf("window scene notify destroy \"%s\"\n", sceneName(windowState.scene))
if err := sceneDestroyer.Destroy(w); err != nil { if err := sceneDestroyer.Destroy(); err != nil {
return err return err
} }
} }
w.scene = scene log.Printf("window scene transition \"%v\" => \"%v\"\n", sceneName(windowState.scene), sceneName(scene))
windowState.scene = scene
if sceneCreator, ok := scene.(SceneCreator); ok { if sceneCreator, ok := scene.(SceneCreator); ok {
log.Printf("scene notify create \"%s\"\n", sceneName(w.scene)) log.Printf("window scene notify create \"%s\"\n", sceneName(windowState.scene))
if err := sceneCreator.Create(w); err != nil { if err := sceneCreator.Create(); err != nil {
return err return err
} }
} }
@ -89,52 +100,41 @@ func (w *Window) SetScene(scene Scene) error {
return nil return nil
} }
func (w *Window) Destroy() error { func WindowDestroy() error {
if w == nil || w.sdlWindow == nil { if !WindowIsCreated() {
return nil return nil
} }
w.makeCurrent() if err := WindowSetScene(nil); err != nil {
if err := w.SetScene(nil); err != nil {
return err return err
} }
if err := w.imguiContext.Destroy(); err != nil { if err := imgui.Destroy(); err != nil {
return err return err
} }
w.imguiContext = nil
sdl.GLDeleteContext(w.sdlGlContext) log.Println("window gl context destroy")
w.sdlGlContext = nil sdl.GLDeleteContext(windowState.sdlGlContext)
windowState.sdlGlContext = nil
if err := w.sdlWindow.Destroy(); err != nil {
return err
}
w.sdlWindow = nil
log.Println("window destroy") log.Println("window destroy")
removeWindow(w) if err := windowState.sdlWindow.Destroy(); err != nil {
return err
}
windowState.sdlWindow = nil
return nil return nil
} }
func (w *Window) CreateTextureRgba(colors []math.Color4b, size math.Vec2i) (graphics.Texture, error) { func WindowRenderTexture(texture graphics.Texture, position math.Vec2i) error {
w.makeCurrent() if !WindowIsCreated() {
return newTextureFromRgba(colors, size) return ErrWindowNotExists
} }
func (w *Window) CreateTextureRgb(colors []math.Color3b, size math.Vec2i) (graphics.Texture, error) {
w.makeCurrent()
return newTextureFromRgb(colors, size)
}
func (w *Window) RenderTexture(texture graphics.Texture, position math.Vec2i) {
size := texture.Size() size := texture.Size()
gl.Enable(gl.TEXTURE_2D) gl.Enable(gl.TEXTURE_2D)
gl.BindTexture(gl.TEXTURE_2D, uint32(texture.Id())) gl.BindTexture(gl.TEXTURE_2D, uint32(texture.Id()))
gl.Begin(gl.QUADS) gl.Begin(gl.QUADS)
gl.TexCoord2f(0, 0) gl.TexCoord2f(0, 0)
gl.Vertex2f(0, 0) gl.Vertex2f(0, 0)
@ -145,31 +145,48 @@ func (w *Window) RenderTexture(texture graphics.Texture, position math.Vec2i) {
gl.TexCoord2f(1, 0) gl.TexCoord2f(1, 0)
gl.Vertex2f(float32(size.X), 0) gl.Vertex2f(float32(size.X), 0)
gl.End() gl.End()
return nil
} }
func (w *Window) DisplaySize() math.Vec2i { func WindowDisplaySize() (math.Vec2i, error) {
width, height := w.sdlWindow.GetSize() if !WindowIsCreated() {
return math.Vec2i{X: int(width), Y: int(height)} return math.Vec2i{}, ErrWindowNotExists
}
width, height := windowState.sdlWindow.GetSize()
return math.Vec2i{X: int(width), Y: int(height)}, nil
} }
func (w *Window) BufferSize() math.Vec2i { func WindowIsCreated() bool {
width, height := w.sdlWindow.GLGetDrawableSize() return windowState.sdlWindow != nil
return math.Vec2i{X: int(width), Y: int(height)}
} }
func (w *Window) Imgui() *imgui.Context { func windowBufferSize() (math.Vec2i, error) {
return w.imguiContext if !WindowIsCreated() {
return math.Vec2i{}, ErrWindowNotExists
}
width, height := windowState.sdlWindow.GLGetDrawableSize()
return math.Vec2i{X: int(width), Y: int(height)}, nil
} }
func (w *Window) advance() (bool, error) { func windowAdvance() (bool, error) {
w.makeCurrent() if !WindowIsCreated() {
return false, ErrWindowNotExists
}
displaySize := w.DisplaySize() displaySize, err := WindowDisplaySize()
w.imguiContext.SetDisplaySize(displaySize) if err != nil {
bufferSize := w.BufferSize() return false, err
w.imguiContext.SetBufferSize(bufferSize) }
w.imguiContext.BeginFrame() bufferSize, err := windowBufferSize()
if err != nil {
return false, err
}
imgui.BeginFrame(displaySize, bufferSize)
gl.Viewport(0, 0, int32(displaySize.X), int32(displaySize.Y)) gl.Viewport(0, 0, int32(displaySize.X), int32(displaySize.Y))
gl.Clear(gl.COLOR_BUFFER_BIT) gl.Clear(gl.COLOR_BUFFER_BIT)
@ -179,23 +196,22 @@ func (w *Window) advance() (bool, error) {
gl.MatrixMode(gl.MODELVIEW) gl.MatrixMode(gl.MODELVIEW)
gl.LoadIdentity() gl.LoadIdentity()
if sceneAdvancer, ok := w.scene.(SceneAdvancer); ok { if sceneAdvancer, ok := windowState.scene.(SceneAdvancer); ok {
if err := sceneAdvancer.Advance(w); err != nil { if err := sceneAdvancer.Advance(); err != nil {
return false, err return false, err
} }
} }
w.imguiContext.EndFrame() imgui.EndFrame()
w.sdlWindow.GLSwap() windowState.sdlWindow.GLSwap()
return w.scene != nil, nil return windowState.scene != nil, nil
} }
func (w *Window) processEvent(event sdl.Event) (bool, error) { func windowProcessEvent(event sdl.Event) (bool, error) {
return w.imguiContext.ProcessEvent(event) if !WindowIsCreated() {
} return false, ErrWindowNotExists
}
func (w *Window) makeCurrent() {
w.sdlWindow.GLMakeCurrent(w.sdlGlContext)
return imgui.ProcessEvent(event)
} }

View File

@ -12,6 +12,7 @@ import (
"github.com/FooSoft/lazarus/graphics" "github.com/FooSoft/lazarus/graphics"
"github.com/FooSoft/lazarus/math" "github.com/FooSoft/lazarus/math"
"github.com/FooSoft/lazarus/platform" "github.com/FooSoft/lazarus/platform"
"github.com/FooSoft/lazarus/platform/imgui"
) )
func loadPalette(path string) (*dat.DatPalette, error) { func loadPalette(path string) (*dat.DatPalette, error) {
@ -45,7 +46,7 @@ func (s *scene) Name() string {
return "viewer" return "viewer"
} }
func (s *scene) Destroy(window *platform.Window) error { func (s *scene) Destroy() error {
if s.texture != nil { if s.texture != nil {
return s.texture.Destroy() return s.texture.Destroy()
} }
@ -53,20 +54,18 @@ func (s *scene) Destroy(window *platform.Window) error {
return nil return nil
} }
func (s *scene) Advance(window *platform.Window) error { func (s *scene) Advance() error {
var ( var (
directionIndex = s.directionIndex directionIndex = s.directionIndex
frameIndex = s.frameIndex frameIndex = s.frameIndex
) )
if s.texture == nil { if s.texture == nil {
if err := s.updateTexture(window); err != nil { if err := s.updateTexture(); err != nil {
return err return err
} }
} }
imgui := window.Imgui()
imgui.DialogBegin("DC6 Viewer") imgui.DialogBegin("DC6 Viewer")
imgui.Image(s.texture) imgui.Image(s.texture)
direction := s.animation.Directions[directionIndex] direction := s.animation.Directions[directionIndex]
@ -78,20 +77,20 @@ func (s *scene) Advance(window *platform.Window) error {
imgui.Text(fmt.Sprintf("Size: %+v", frame.Size)) imgui.Text(fmt.Sprintf("Size: %+v", frame.Size))
imgui.Text(fmt.Sprintf("Offset: %+v", frame.Offset)) imgui.Text(fmt.Sprintf("Offset: %+v", frame.Offset))
if imgui.Button("Exit") { if imgui.Button("Exit") {
window.SetScene(nil) platform.WindowSetScene(nil)
} }
imgui.DialogEnd() imgui.DialogEnd()
if directionIndex != s.directionIndex || frameIndex != s.frameIndex { if directionIndex != s.directionIndex || frameIndex != s.frameIndex {
s.directionIndex = directionIndex s.directionIndex = directionIndex
s.frameIndex = frameIndex s.frameIndex = frameIndex
s.updateTexture(window) s.updateTexture()
} }
return nil return nil
} }
func (s *scene) updateTexture(window *platform.Window) error { func (s *scene) updateTexture() error {
frame := s.animation.Directions[s.directionIndex].Frames[s.frameIndex] frame := s.animation.Directions[s.directionIndex].Frames[s.frameIndex]
colors := make([]math.Color3b, frame.Size.X*frame.Size.Y) colors := make([]math.Color3b, frame.Size.X*frame.Size.Y)
for y := 0; y < frame.Size.Y; y++ { for y := 0; y < frame.Size.Y; y++ {
@ -107,7 +106,7 @@ func (s *scene) updateTexture(window *platform.Window) error {
} }
var err error var err error
s.texture, err = window.CreateTextureRgb(colors, frame.Size) s.texture, err = platform.NewTextureFromRgb(colors, frame.Size)
if err != nil { if err != nil {
return err return err
} }
@ -151,12 +150,11 @@ func main() {
} }
scene := &scene{animation: animation, palette: palette} scene := &scene{animation: animation, palette: palette}
window, err := platform.NewWindow("Viewer", math.Vec2i{X: 1024, Y: 768}, scene) if err := platform.WindowCreate("Viewer", math.Vec2i{X: 1024, Y: 768}, scene); err != nil {
if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
os.Exit(1) os.Exit(1)
} }
defer window.Destroy() defer platform.WindowDestroy()
for { for {
run, err := platform.Advance() run, err := platform.Advance()