imgui cleanup
This commit is contained in:
parent
6c398bce8e
commit
2a22e7a446
104
platform/imgui_backend/context.go
Normal file
104
platform/imgui_backend/context.go
Normal file
@ -0,0 +1,104 @@
|
||||
package imgui_backend
|
||||
|
||||
import (
|
||||
imgui "github.com/FooSoft/imgui-go"
|
||||
"github.com/FooSoft/lazarus/math"
|
||||
"github.com/go-gl/gl/v2.1/gl"
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
)
|
||||
|
||||
type Context struct {
|
||||
buttonsDown [3]bool
|
||||
lastTime uint64
|
||||
fontTexture uint32
|
||||
displaySize math.Vec2i
|
||||
bufferSize math.Vec2i
|
||||
}
|
||||
|
||||
func CreateContext(displaySize, bufferSize math.Vec2i) (*Context, error) {
|
||||
singleton.refCount++
|
||||
if singleton.refCount == 1 {
|
||||
singleton.context = imgui.CreateContext(nil)
|
||||
keys := map[int]int{
|
||||
imgui.KeyTab: sdl.SCANCODE_TAB,
|
||||
imgui.KeyLeftArrow: sdl.SCANCODE_LEFT,
|
||||
imgui.KeyRightArrow: sdl.SCANCODE_RIGHT,
|
||||
imgui.KeyUpArrow: sdl.SCANCODE_UP,
|
||||
imgui.KeyDownArrow: sdl.SCANCODE_DOWN,
|
||||
imgui.KeyPageUp: sdl.SCANCODE_PAGEUP,
|
||||
imgui.KeyPageDown: sdl.SCANCODE_PAGEDOWN,
|
||||
imgui.KeyHome: sdl.SCANCODE_HOME,
|
||||
imgui.KeyEnd: sdl.SCANCODE_END,
|
||||
imgui.KeyInsert: sdl.SCANCODE_INSERT,
|
||||
imgui.KeyDelete: sdl.SCANCODE_DELETE,
|
||||
imgui.KeyBackspace: sdl.SCANCODE_BACKSPACE,
|
||||
imgui.KeySpace: sdl.SCANCODE_BACKSPACE,
|
||||
imgui.KeyEnter: sdl.SCANCODE_RETURN,
|
||||
imgui.KeyEscape: sdl.SCANCODE_ESCAPE,
|
||||
imgui.KeyA: sdl.SCANCODE_A,
|
||||
imgui.KeyC: sdl.SCANCODE_C,
|
||||
imgui.KeyV: sdl.SCANCODE_V,
|
||||
imgui.KeyX: sdl.SCANCODE_X,
|
||||
imgui.KeyY: sdl.SCANCODE_Y,
|
||||
imgui.KeyZ: sdl.SCANCODE_Z,
|
||||
}
|
||||
|
||||
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
|
||||
io := imgui.CurrentIO()
|
||||
for imguiKey, nativeKey := range keys {
|
||||
io.KeyMap(imguiKey, nativeKey)
|
||||
}
|
||||
}
|
||||
|
||||
c := &Context{displaySize: displaySize, bufferSize: bufferSize}
|
||||
|
||||
// Build texture atlas
|
||||
io := imgui.CurrentIO()
|
||||
image := io.Fonts().TextureDataRGBA32()
|
||||
|
||||
// Store state
|
||||
var lastTexture int32
|
||||
gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
|
||||
|
||||
// Create texture
|
||||
gl.GenTextures(1, &c.fontTexture)
|
||||
gl.BindTexture(gl.TEXTURE_2D, c.fontTexture)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0)
|
||||
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(image.Width), int32(image.Height), 0, gl.RGBA, gl.UNSIGNED_BYTE, image.Pixels)
|
||||
|
||||
// Restore state
|
||||
gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture))
|
||||
|
||||
// Store texture identifier
|
||||
io.Fonts().SetTextureID(imgui.TextureID(c.fontTexture))
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Context) SetDisplaySize(displaySize math.Vec2i) {
|
||||
c.displaySize = displaySize
|
||||
}
|
||||
|
||||
func (c *Context) SetBufferSize(bufferSize math.Vec2i) {
|
||||
c.bufferSize = bufferSize
|
||||
}
|
||||
|
||||
func (c *Context) Destroy() error {
|
||||
if c == nil || c.fontTexture == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
gl.DeleteTextures(1, &c.fontTexture)
|
||||
imgui.CurrentIO().Fonts().SetTextureID(0)
|
||||
c.fontTexture = 0
|
||||
|
||||
singleton.refCount--
|
||||
if singleton.refCount == 0 {
|
||||
singleton.context.Destroy()
|
||||
singleton.context = nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
@ -1,120 +1,41 @@
|
||||
package imgui_backend
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"unsafe"
|
||||
|
||||
"github.com/FooSoft/imgui-go"
|
||||
"github.com/FooSoft/lazarus/math"
|
||||
"github.com/go-gl/gl/v2.1/gl"
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrAlreadyInit = errors.New("imgui backend is already initialized")
|
||||
ErrWasNotInit = errors.New("imgui backend was not initialized")
|
||||
)
|
||||
|
||||
var singleton struct {
|
||||
isInit bool
|
||||
buttonsDown [3]bool
|
||||
lastTime uint64
|
||||
fontTexture uint32
|
||||
context *imgui.Context
|
||||
|
||||
windowSize math.Vec2i
|
||||
bufferSize math.Vec2i
|
||||
refCount int
|
||||
}
|
||||
|
||||
func Init() error {
|
||||
if singleton.isInit {
|
||||
return ErrAlreadyInit
|
||||
}
|
||||
|
||||
singleton.context = imgui.CreateContext(nil)
|
||||
|
||||
keys := map[int]int{
|
||||
imgui.KeyTab: sdl.SCANCODE_TAB,
|
||||
imgui.KeyLeftArrow: sdl.SCANCODE_LEFT,
|
||||
imgui.KeyRightArrow: sdl.SCANCODE_RIGHT,
|
||||
imgui.KeyUpArrow: sdl.SCANCODE_UP,
|
||||
imgui.KeyDownArrow: sdl.SCANCODE_DOWN,
|
||||
imgui.KeyPageUp: sdl.SCANCODE_PAGEUP,
|
||||
imgui.KeyPageDown: sdl.SCANCODE_PAGEDOWN,
|
||||
imgui.KeyHome: sdl.SCANCODE_HOME,
|
||||
imgui.KeyEnd: sdl.SCANCODE_END,
|
||||
imgui.KeyInsert: sdl.SCANCODE_INSERT,
|
||||
imgui.KeyDelete: sdl.SCANCODE_DELETE,
|
||||
imgui.KeyBackspace: sdl.SCANCODE_BACKSPACE,
|
||||
imgui.KeySpace: sdl.SCANCODE_BACKSPACE,
|
||||
imgui.KeyEnter: sdl.SCANCODE_RETURN,
|
||||
imgui.KeyEscape: sdl.SCANCODE_ESCAPE,
|
||||
imgui.KeyA: sdl.SCANCODE_A,
|
||||
imgui.KeyC: sdl.SCANCODE_C,
|
||||
imgui.KeyV: sdl.SCANCODE_V,
|
||||
imgui.KeyX: sdl.SCANCODE_X,
|
||||
imgui.KeyY: sdl.SCANCODE_Y,
|
||||
imgui.KeyZ: sdl.SCANCODE_Z,
|
||||
}
|
||||
|
||||
// Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
|
||||
io := imgui.CurrentIO()
|
||||
for imguiKey, nativeKey := range keys {
|
||||
io.KeyMap(imguiKey, nativeKey)
|
||||
}
|
||||
|
||||
singleton.isInit = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Shutdown() error {
|
||||
if !singleton.isInit {
|
||||
return ErrWasNotInit
|
||||
}
|
||||
|
||||
singleton.isInit = false
|
||||
|
||||
destroyFontTexture(singleton.fontTexture)
|
||||
singleton.fontTexture = 0
|
||||
|
||||
singleton.context.Destroy()
|
||||
singleton.context = nil
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func BeginFrame(windowSize, bufferSize math.Vec2i) error {
|
||||
if !singleton.isInit {
|
||||
return ErrWasNotInit
|
||||
}
|
||||
|
||||
singleton.windowSize = windowSize
|
||||
singleton.bufferSize = bufferSize
|
||||
|
||||
if singleton.fontTexture == 0 {
|
||||
singleton.fontTexture = createFontTexture()
|
||||
}
|
||||
|
||||
func (c *Context) BeginFrame() error {
|
||||
// Setup display size (every frame to accommodate for window resizing)
|
||||
io := imgui.CurrentIO()
|
||||
io.SetDisplaySize(imgui.Vec2{X: float32(windowSize.X), Y: float32(windowSize.Y)})
|
||||
io.SetDisplaySize(imgui.Vec2{
|
||||
X: float32(c.displaySize.X),
|
||||
Y: float32(c.displaySize.Y),
|
||||
})
|
||||
|
||||
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
|
||||
frequency := sdl.GetPerformanceFrequency()
|
||||
currentTime := sdl.GetPerformanceCounter()
|
||||
if singleton.lastTime > 0 {
|
||||
io.SetDeltaTime(float32(currentTime-singleton.lastTime) / float32(frequency))
|
||||
if c.lastTime > 0 {
|
||||
io.SetDeltaTime(float32(currentTime-c.lastTime) / float32(frequency))
|
||||
} else {
|
||||
io.SetDeltaTime(1.0 / 60.0)
|
||||
}
|
||||
singleton.lastTime = currentTime
|
||||
c.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.
|
||||
x, y, state := sdl.GetMouseState()
|
||||
for i, button := range []uint32{sdl.BUTTON_LEFT, sdl.BUTTON_RIGHT, sdl.BUTTON_MIDDLE} {
|
||||
io.SetMouseButtonDown(i, singleton.buttonsDown[i] || (state&sdl.Button(button)) != 0)
|
||||
singleton.buttonsDown[i] = false
|
||||
io.SetMouseButtonDown(i, c.buttonsDown[i] || (state&sdl.Button(button)) != 0)
|
||||
c.buttonsDown[i] = false
|
||||
}
|
||||
|
||||
io.SetMousePosition(imgui.Vec2{X: float32(x), Y: float32(y)})
|
||||
@ -128,11 +49,7 @@ func BeginFrame(windowSize, bufferSize math.Vec2i) error {
|
||||
// - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application.
|
||||
// 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.
|
||||
func ProcessEvent(event sdl.Event) (bool, error) {
|
||||
if !singleton.isInit {
|
||||
return false, ErrWasNotInit
|
||||
}
|
||||
|
||||
func (c *Context) ProcessEvent(event sdl.Event) (bool, error) {
|
||||
switch io := imgui.CurrentIO(); event.GetType() {
|
||||
case sdl.MOUSEWHEEL:
|
||||
wheelEvent := event.(*sdl.MouseWheelEvent)
|
||||
@ -152,13 +69,13 @@ func ProcessEvent(event sdl.Event) (bool, error) {
|
||||
buttonEvent := event.(*sdl.MouseButtonEvent)
|
||||
switch buttonEvent.Button {
|
||||
case sdl.BUTTON_LEFT:
|
||||
singleton.buttonsDown[0] = true
|
||||
c.buttonsDown[0] = true
|
||||
break
|
||||
case sdl.BUTTON_RIGHT:
|
||||
singleton.buttonsDown[1] = true
|
||||
c.buttonsDown[1] = true
|
||||
break
|
||||
case sdl.BUTTON_MIDDLE:
|
||||
singleton.buttonsDown[2] = true
|
||||
c.buttonsDown[2] = true
|
||||
break
|
||||
}
|
||||
return true, nil
|
||||
@ -188,16 +105,12 @@ func ProcessEvent(event sdl.Event) (bool, error) {
|
||||
|
||||
// OpenGL2 Render function.
|
||||
// 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 EndFrame() error {
|
||||
if !singleton.isInit {
|
||||
return ErrWasNotInit
|
||||
}
|
||||
|
||||
func (c *Context) EndFrame() error {
|
||||
imgui.Render()
|
||||
drawData := imgui.RenderedDrawData()
|
||||
drawData.ScaleClipRects(imgui.Vec2{
|
||||
X: float32(singleton.bufferSize.X) / float32(singleton.windowSize.X),
|
||||
Y: float32(singleton.bufferSize.Y) / float32(singleton.windowSize.Y),
|
||||
X: float32(c.bufferSize.X) / float32(c.displaySize.X),
|
||||
Y: float32(c.bufferSize.Y) / float32(c.displaySize.Y),
|
||||
})
|
||||
|
||||
// We are using the OpenGL fixed pipeline to make the example code simpler to read!
|
||||
@ -229,11 +142,11 @@ func EndFrame() error {
|
||||
|
||||
// Setup viewport, orthographic projection matrix
|
||||
// Our visible imgui space lies from draw_data->DisplayPos (top left) to draw_data->DisplayPos+data_data->DisplaySize (bottom right). DisplayMin is typically (0,0) for single viewport apps.
|
||||
gl.Viewport(0, 0, int32(singleton.bufferSize.X), int32(singleton.bufferSize.Y))
|
||||
gl.Viewport(0, 0, int32(c.bufferSize.X), int32(c.bufferSize.Y))
|
||||
gl.MatrixMode(gl.PROJECTION)
|
||||
gl.PushMatrix()
|
||||
gl.LoadIdentity()
|
||||
gl.Ortho(0, float64(singleton.windowSize.X), float64(singleton.windowSize.Y), 0, -1, 1)
|
||||
gl.Ortho(0, float64(c.displaySize.X), float64(c.displaySize.Y), 0, -1, 1)
|
||||
gl.MatrixMode(gl.MODELVIEW)
|
||||
gl.PushMatrix()
|
||||
gl.LoadIdentity()
|
||||
@ -261,7 +174,7 @@ func EndFrame() error {
|
||||
command.CallUserCallback(commandList)
|
||||
} else {
|
||||
clipRect := command.ClipRect()
|
||||
gl.Scissor(int32(clipRect.X), int32(singleton.bufferSize.Y)-int32(clipRect.W), int32(clipRect.Z-clipRect.X), int32(clipRect.W-clipRect.Y))
|
||||
gl.Scissor(int32(clipRect.X), int32(c.bufferSize.Y)-int32(clipRect.W), int32(clipRect.Z-clipRect.X), int32(clipRect.W-clipRect.Y))
|
||||
gl.BindTexture(gl.TEXTURE_2D, uint32(command.TextureID()))
|
||||
gl.DrawElements(gl.TRIANGLES, int32(command.ElementCount()), uint32(drawType), unsafe.Pointer(indexBufferOffset))
|
||||
}
|
||||
@ -287,34 +200,3 @@ func EndFrame() error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func createFontTexture() uint32 {
|
||||
// Build texture atlas
|
||||
io := imgui.CurrentIO()
|
||||
image := io.Fonts().TextureDataRGBA32()
|
||||
|
||||
// Upload texture to graphics system
|
||||
var lastTexture int32
|
||||
gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
|
||||
var fontTexture uint32
|
||||
gl.GenTextures(1, &fontTexture)
|
||||
gl.BindTexture(gl.TEXTURE_2D, fontTexture)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
|
||||
gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
|
||||
gl.PixelStorei(gl.UNPACK_ROW_LENGTH, 0)
|
||||
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(image.Width), int32(image.Height), 0, gl.RGBA, gl.UNSIGNED_BYTE, image.Pixels)
|
||||
|
||||
// Store our identifier
|
||||
io.Fonts().SetTextureID(imgui.TextureID(fontTexture))
|
||||
|
||||
// Restore state
|
||||
gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture))
|
||||
return fontTexture
|
||||
}
|
||||
|
||||
func destroyFontTexture(fontTexture uint32) {
|
||||
if fontTexture != 0 {
|
||||
gl.DeleteTextures(1, &fontTexture)
|
||||
imgui.CurrentIO().Fonts().SetTextureID(0)
|
||||
}
|
||||
}
|
||||
|
@ -5,7 +5,6 @@ import (
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/FooSoft/lazarus/platform/imgui_backend"
|
||||
"github.com/go-gl/gl/v2.1/gl"
|
||||
"github.com/veandco/go-sdl2/sdl"
|
||||
)
|
||||
@ -43,10 +42,6 @@ func Init() error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := imgui_backend.Init(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
state.isInit = true
|
||||
return nil
|
||||
}
|
||||
@ -62,10 +57,6 @@ func Shutdown() error {
|
||||
}
|
||||
}
|
||||
|
||||
if err := imgui_backend.Shutdown(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
state.windows = nil
|
||||
state.isInit = false
|
||||
|
||||
@ -115,15 +106,17 @@ func advanceWindows() {
|
||||
}
|
||||
|
||||
func processEvent(event sdl.Event) bool {
|
||||
handled, _ := imgui_backend.ProcessEvent(event)
|
||||
for _, window := range state.windows {
|
||||
handled, _ := window.processEvent(event)
|
||||
if handled {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
switch event.(type) {
|
||||
case *sdl.QuitEvent:
|
||||
return false
|
||||
}
|
||||
|
||||
default:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,15 @@ import (
|
||||
type Window struct {
|
||||
sdlWindow *sdl.Window
|
||||
sdlGlContext sdl.GLContext
|
||||
imguiContext *imgui_backend.Context
|
||||
scene Scene
|
||||
}
|
||||
|
||||
func newWindow(title string, width, height int, scene Scene) (*Window, error) {
|
||||
sdl.GLSetAttribute(sdl.GL_CONTEXT_MAJOR_VERSION, 2)
|
||||
sdl.GLSetAttribute(sdl.GL_CONTEXT_MINOR_VERSION, 1)
|
||||
sdl.GLSetAttribute(sdl.GL_DOUBLEBUFFER, 1)
|
||||
|
||||
sdlWindow, err := sdl.CreateWindow(
|
||||
title,
|
||||
sdl.WINDOWPOS_CENTERED,
|
||||
@ -32,11 +37,18 @@ func newWindow(title string, width, height int, scene Scene) (*Window, error) {
|
||||
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)
|
||||
w := &Window{
|
||||
sdlWindow: sdlWindow,
|
||||
sdlGlContext: sdlGlContext,
|
||||
scene: scene,
|
||||
}
|
||||
|
||||
w.imguiContext, err = imgui_backend.CreateContext(w.DisplaySize(), w.BufferSize())
|
||||
if err != nil {
|
||||
w.Destroy()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w := &Window{sdlWindow, sdlGlContext, scene}
|
||||
if err := scene.Init(w); err != nil {
|
||||
w.Destroy()
|
||||
return nil, err
|
||||
@ -46,7 +58,7 @@ func newWindow(title string, width, height int, scene Scene) (*Window, error) {
|
||||
}
|
||||
|
||||
func (w *Window) Destroy() error {
|
||||
if w.sdlWindow == nil {
|
||||
if w == nil || w.sdlWindow == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -92,12 +104,25 @@ func (w *Window) RenderTexture(texture *Texture, position math.Vec2i) {
|
||||
gl.End()
|
||||
}
|
||||
|
||||
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)}
|
||||
}
|
||||
|
||||
func (w *Window) advance() {
|
||||
w.sdlWindow.GLMakeCurrent(w.sdlGlContext)
|
||||
|
||||
displaySize := w.displaySize()
|
||||
bufferSize := w.bufferSize()
|
||||
imgui_backend.BeginFrame(displaySize, bufferSize)
|
||||
displaySize := w.DisplaySize()
|
||||
w.imguiContext.SetDisplaySize(displaySize)
|
||||
bufferSize := w.BufferSize()
|
||||
w.imguiContext.SetBufferSize(bufferSize)
|
||||
|
||||
w.imguiContext.BeginFrame()
|
||||
|
||||
gl.Viewport(0, 0, int32(displaySize.X), int32(displaySize.Y))
|
||||
gl.Clear(gl.COLOR_BUFFER_BIT)
|
||||
@ -109,16 +134,10 @@ func (w *Window) advance() {
|
||||
|
||||
w.scene.Advance(w)
|
||||
|
||||
imgui_backend.EndFrame()
|
||||
w.imguiContext.EndFrame()
|
||||
w.sdlWindow.GLSwap()
|
||||
}
|
||||
|
||||
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)}
|
||||
func (w *Window) processEvent(event sdl.Event) (bool, error) {
|
||||
return w.imguiContext.ProcessEvent(event)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user