switch imgui backend to cimgui
This commit is contained in:
parent
8c28fc6839
commit
ad7067101a
2
.gitmodules
vendored
2
.gitmodules
vendored
@ -2,5 +2,5 @@
|
|||||||
path = formats/mpq/stormlib
|
path = formats/mpq/stormlib
|
||||||
url = https://github.com/ladislav-zezula/StormLib.git
|
url = https://github.com/ladislav-zezula/StormLib.git
|
||||||
[submodule "platform/imgui/cimgui"]
|
[submodule "platform/imgui/cimgui"]
|
||||||
path = platform/imgui/imgui_wrapper/cimgui
|
path = platform/imgui/cimgui
|
||||||
url = https://github.com/cimgui/cimgui.git
|
url = https://github.com/cimgui/cimgui.git
|
||||||
|
@ -1,14 +1,58 @@
|
|||||||
package imgui
|
package imgui
|
||||||
|
|
||||||
|
// #cgo linux CFLAGS: -I./cimgui
|
||||||
|
// #cgo linux LDFLAGS: -L./cimgui -l:cimgui.a -lstdc++ -lm
|
||||||
|
// #include "native.h"
|
||||||
|
import "C"
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
imgui "github.com/FooSoft/imgui-go"
|
|
||||||
"github.com/FooSoft/lazarus/math"
|
"github.com/FooSoft/lazarus/math"
|
||||||
"github.com/go-gl/gl/v2.1/gl"
|
"github.com/go-gl/gl/v2.1/gl"
|
||||||
"github.com/veandco/go-sdl2/sdl"
|
"github.com/veandco/go-sdl2/sdl"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
pointerSize = unsafe.Sizeof(C.uintptr_t(0))
|
||||||
|
drawCommandSize = unsafe.Sizeof(C.ImDrawCmd{})
|
||||||
|
indexSize = unsafe.Sizeof(C.ImDrawIdx(0))
|
||||||
|
vertexSize = unsafe.Sizeof(C.ImDrawVert{})
|
||||||
|
vertexOffsetPos = unsafe.Offsetof(C.ImDrawVert{}.pos)
|
||||||
|
vertexOffsetUv = unsafe.Offsetof(C.ImDrawVert{}.uv)
|
||||||
|
vertexOffsetCol = unsafe.Offsetof(C.ImDrawVert{}.col)
|
||||||
|
)
|
||||||
|
|
||||||
|
var keyMapping = map[int]C.int{
|
||||||
|
C.ImGuiKey_Tab: sdl.SCANCODE_TAB,
|
||||||
|
C.ImGuiKey_LeftArrow: sdl.SCANCODE_LEFT,
|
||||||
|
C.ImGuiKey_RightArrow: sdl.SCANCODE_RIGHT,
|
||||||
|
C.ImGuiKey_UpArrow: sdl.SCANCODE_UP,
|
||||||
|
C.ImGuiKey_DownArrow: sdl.SCANCODE_DOWN,
|
||||||
|
C.ImGuiKey_PageUp: sdl.SCANCODE_PAGEUP,
|
||||||
|
C.ImGuiKey_PageDown: sdl.SCANCODE_PAGEDOWN,
|
||||||
|
C.ImGuiKey_Home: sdl.SCANCODE_HOME,
|
||||||
|
C.ImGuiKey_End: sdl.SCANCODE_END,
|
||||||
|
C.ImGuiKey_Insert: sdl.SCANCODE_INSERT,
|
||||||
|
C.ImGuiKey_Delete: sdl.SCANCODE_DELETE,
|
||||||
|
C.ImGuiKey_Backspace: sdl.SCANCODE_BACKSPACE,
|
||||||
|
C.ImGuiKey_Space: sdl.SCANCODE_BACKSPACE,
|
||||||
|
C.ImGuiKey_Enter: sdl.SCANCODE_RETURN,
|
||||||
|
C.ImGuiKey_Escape: sdl.SCANCODE_ESCAPE,
|
||||||
|
C.ImGuiKey_A: sdl.SCANCODE_A,
|
||||||
|
C.ImGuiKey_C: sdl.SCANCODE_C,
|
||||||
|
C.ImGuiKey_V: sdl.SCANCODE_V,
|
||||||
|
C.ImGuiKey_X: sdl.SCANCODE_X,
|
||||||
|
C.ImGuiKey_Y: sdl.SCANCODE_Y,
|
||||||
|
C.ImGuiKey_Z: sdl.SCANCODE_Z,
|
||||||
|
}
|
||||||
|
|
||||||
|
var singleton struct {
|
||||||
|
nativeContext *C.ImGuiContext
|
||||||
|
nativeIo *C.ImGuiIO
|
||||||
|
refCount int
|
||||||
|
}
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
buttonsDown [3]bool
|
buttonsDown [3]bool
|
||||||
lastTime uint64
|
lastTime uint64
|
||||||
@ -21,77 +65,55 @@ func New(displaySize, bufferSize math.Vec2i) (*Context, error) {
|
|||||||
singleton.refCount++
|
singleton.refCount++
|
||||||
if singleton.refCount == 1 {
|
if singleton.refCount == 1 {
|
||||||
log.Println("imgui global create")
|
log.Println("imgui global create")
|
||||||
singleton.context = imgui.CreateContext(nil)
|
singleton.nativeContext = C.igCreateContext(nil)
|
||||||
|
singleton.nativeIo = C.igGetIO()
|
||||||
|
|
||||||
keys := map[int]int{
|
for imguiKey, nativeKey := range keyMapping {
|
||||||
imgui.KeyTab: sdl.SCANCODE_TAB,
|
singleton.nativeIo.KeyMap[imguiKey] = nativeKey
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("imgui context create")
|
log.Println("imgui context create")
|
||||||
c := &Context{displaySize: displaySize, bufferSize: bufferSize}
|
c := &Context{displaySize: displaySize, bufferSize: bufferSize}
|
||||||
|
|
||||||
// Build texture atlas
|
var imageData *C.uchar
|
||||||
io := imgui.CurrentIO()
|
var imageWidth, imageHeight C.int
|
||||||
image := io.Fonts().TextureDataRGBA32()
|
C.ImFontAtlas_GetTexDataAsRGBA32(singleton.nativeIo.Fonts, &imageData, &imageWidth, &imageHeight, nil)
|
||||||
|
|
||||||
// Store state
|
|
||||||
var lastTexture int32
|
var lastTexture int32
|
||||||
gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
|
gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
|
||||||
|
|
||||||
// Create texture
|
|
||||||
gl.GenTextures(1, &c.fontTexture)
|
gl.GenTextures(1, &c.fontTexture)
|
||||||
gl.BindTexture(gl.TEXTURE_2D, 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_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.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(imageWidth), int32(imageHeight), 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(imageData))
|
||||||
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))
|
gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture))
|
||||||
|
|
||||||
// Store texture identifier
|
|
||||||
io.Fonts().SetTextureID(imgui.TextureID(c.fontTexture))
|
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Context) Destroy() error {
|
||||||
|
if c == nil || c.fontTexture == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("imgui context destroy")
|
||||||
|
gl.DeleteTextures(1, &c.fontTexture)
|
||||||
|
singleton.nativeIo.Fonts.TexID = C.nativeHandleCast(C.uintptr_t(c.fontTexture))
|
||||||
|
c.fontTexture = 0
|
||||||
|
|
||||||
|
singleton.refCount--
|
||||||
|
if singleton.refCount == 0 {
|
||||||
|
log.Println("imgui global destroy")
|
||||||
|
C.igDestroyContext(singleton.nativeContext)
|
||||||
|
singleton.nativeContext = nil
|
||||||
|
singleton.nativeIo = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Context) SetDisplaySize(displaySize math.Vec2i) {
|
func (c *Context) SetDisplaySize(displaySize math.Vec2i) {
|
||||||
c.displaySize = displaySize
|
c.displaySize = displaySize
|
||||||
}
|
}
|
||||||
@ -100,22 +122,171 @@ func (c *Context) SetBufferSize(bufferSize math.Vec2i) {
|
|||||||
c.bufferSize = bufferSize
|
c.bufferSize = bufferSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) Destroy() error {
|
func (c *Context) BeginFrame() {
|
||||||
if c == nil || c.fontTexture == 0 {
|
singleton.nativeIo.Fonts.TexID = C.nativeHandleCast(C.uintptr_t(c.fontTexture))
|
||||||
return nil
|
singleton.nativeIo.DisplaySize.x = C.float(c.displaySize.X)
|
||||||
|
singleton.nativeIo.DisplaySize.y = C.float(c.displaySize.Y)
|
||||||
|
|
||||||
|
currentTime := sdl.GetPerformanceCounter()
|
||||||
|
if c.lastTime > 0 {
|
||||||
|
singleton.nativeIo.DeltaTime = C.float(float32(currentTime-c.lastTime) / float32(sdl.GetPerformanceFrequency()))
|
||||||
|
} else {
|
||||||
|
singleton.nativeIo.DeltaTime = C.float(1.0 / 60.0)
|
||||||
|
}
|
||||||
|
c.lastTime = currentTime
|
||||||
|
|
||||||
|
x, y, state := sdl.GetMouseState()
|
||||||
|
singleton.nativeIo.MousePos.x = C.float(x)
|
||||||
|
singleton.nativeIo.MousePos.y = C.float(y)
|
||||||
|
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)
|
||||||
|
c.buttonsDown[i] = false
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("imgui context destroy")
|
C.igNewFrame()
|
||||||
gl.DeleteTextures(1, &c.fontTexture)
|
}
|
||||||
imgui.CurrentIO().Fonts().SetTextureID(0)
|
|
||||||
c.fontTexture = 0
|
|
||||||
|
|
||||||
singleton.refCount--
|
func (c *Context) ProcessEvent(event sdl.Event) (bool, error) {
|
||||||
if singleton.refCount == 0 {
|
switch event.GetType() {
|
||||||
log.Println("imgui global destroy")
|
case sdl.MOUSEWHEEL:
|
||||||
singleton.context.Destroy()
|
wheelEvent := event.(*sdl.MouseWheelEvent)
|
||||||
singleton.context = nil
|
if wheelEvent.X > 0 {
|
||||||
|
singleton.nativeIo.MouseDelta.x++
|
||||||
|
} else if wheelEvent.X < 0 {
|
||||||
|
singleton.nativeIo.MouseDelta.x--
|
||||||
}
|
}
|
||||||
|
if wheelEvent.Y > 0 {
|
||||||
|
singleton.nativeIo.MouseDelta.y++
|
||||||
|
} else if wheelEvent.Y < 0 {
|
||||||
|
singleton.nativeIo.MouseDelta.y--
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
case sdl.MOUSEBUTTONDOWN:
|
||||||
|
buttonEvent := event.(*sdl.MouseButtonEvent)
|
||||||
|
switch buttonEvent.Button {
|
||||||
|
case sdl.BUTTON_LEFT:
|
||||||
|
c.buttonsDown[0] = true
|
||||||
|
break
|
||||||
|
case sdl.BUTTON_RIGHT:
|
||||||
|
c.buttonsDown[1] = true
|
||||||
|
break
|
||||||
|
case sdl.BUTTON_MIDDLE:
|
||||||
|
c.buttonsDown[2] = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
case sdl.TEXTINPUT:
|
||||||
|
inputEvent := event.(*sdl.TextInputEvent)
|
||||||
|
C.ImGuiIO_AddInputCharactersUTF8(singleton.nativeIo, (*C.char)(unsafe.Pointer(&inputEvent.Text[0])))
|
||||||
|
return true, nil
|
||||||
|
case sdl.KEYDOWN:
|
||||||
|
keyEvent := event.(*sdl.KeyboardEvent)
|
||||||
|
singleton.nativeIo.KeysDown[keyEvent.Keysym.Scancode] = true
|
||||||
|
modState := sdl.GetModState()
|
||||||
|
singleton.nativeIo.KeyCtrl = C.bool(modState&sdl.KMOD_CTRL != 0)
|
||||||
|
singleton.nativeIo.KeyAlt = C.bool(modState&sdl.KMOD_ALT != 0)
|
||||||
|
singleton.nativeIo.KeyShift = C.bool(modState&sdl.KMOD_SHIFT != 0)
|
||||||
|
case sdl.KEYUP:
|
||||||
|
keyEvent := event.(*sdl.KeyboardEvent)
|
||||||
|
singleton.nativeIo.KeysDown[keyEvent.Keysym.Scancode] = false
|
||||||
|
modState := sdl.GetModState()
|
||||||
|
singleton.nativeIo.KeyCtrl = C.bool(modState&sdl.KMOD_CTRL != 0)
|
||||||
|
singleton.nativeIo.KeyAlt = C.bool(modState&sdl.KMOD_ALT != 0)
|
||||||
|
singleton.nativeIo.KeyShift = C.bool(modState&sdl.KMOD_SHIFT != 0)
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) EndFrame() error {
|
||||||
|
C.igRender()
|
||||||
|
|
||||||
|
var lastTexture int32
|
||||||
|
gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
|
||||||
|
var lastPolygonMode [2]int32
|
||||||
|
gl.GetIntegerv(gl.POLYGON_MODE, &lastPolygonMode[0])
|
||||||
|
var lastViewport [4]int32
|
||||||
|
gl.GetIntegerv(gl.VIEWPORT, &lastViewport[0])
|
||||||
|
var lastScissorBox [4]int32
|
||||||
|
gl.GetIntegerv(gl.SCISSOR_BOX, &lastScissorBox[0])
|
||||||
|
gl.PushAttrib(gl.ENABLE_BIT | gl.COLOR_BUFFER_BIT | gl.TRANSFORM_BIT)
|
||||||
|
gl.Enable(gl.BLEND)
|
||||||
|
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
||||||
|
gl.Disable(gl.CULL_FACE)
|
||||||
|
gl.Disable(gl.DEPTH_TEST)
|
||||||
|
gl.Disable(gl.LIGHTING)
|
||||||
|
gl.Disable(gl.COLOR_MATERIAL)
|
||||||
|
gl.Enable(gl.SCISSOR_TEST)
|
||||||
|
gl.EnableClientState(gl.VERTEX_ARRAY)
|
||||||
|
gl.EnableClientState(gl.TEXTURE_COORD_ARRAY)
|
||||||
|
gl.EnableClientState(gl.COLOR_ARRAY)
|
||||||
|
gl.Enable(gl.TEXTURE_2D)
|
||||||
|
gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL)
|
||||||
|
|
||||||
|
gl.Viewport(0, 0, int32(c.bufferSize.X), int32(c.bufferSize.Y))
|
||||||
|
gl.MatrixMode(gl.PROJECTION)
|
||||||
|
gl.PushMatrix()
|
||||||
|
gl.LoadIdentity()
|
||||||
|
gl.Ortho(0, float64(c.displaySize.X), float64(c.displaySize.Y), 0, -1, 1)
|
||||||
|
gl.MatrixMode(gl.MODELVIEW)
|
||||||
|
gl.PushMatrix()
|
||||||
|
gl.LoadIdentity()
|
||||||
|
|
||||||
|
drawData := C.igGetDrawData()
|
||||||
|
C.ImDrawData_ScaleClipRects(
|
||||||
|
drawData,
|
||||||
|
C.ImVec2{
|
||||||
|
x: C.float(float32(c.bufferSize.X) / float32(c.displaySize.X)),
|
||||||
|
y: C.float(float32(c.bufferSize.Y) / float32(c.displaySize.Y)),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
for i := C.int(0); i < drawData.CmdListsCount; i++ {
|
||||||
|
var (
|
||||||
|
commandList = *(**C.ImDrawList)(unsafe.Pointer(uintptr(unsafe.Pointer(drawData.CmdLists)) + pointerSize*uintptr(i)))
|
||||||
|
vertexBuffer = unsafe.Pointer(commandList.VtxBuffer.Data)
|
||||||
|
indexBuffer = unsafe.Pointer(commandList.IdxBuffer.Data)
|
||||||
|
elementCount = C.unsigned(0)
|
||||||
|
)
|
||||||
|
|
||||||
|
gl.VertexPointer(2, gl.FLOAT, int32(vertexSize), unsafe.Pointer(uintptr(vertexBuffer)+uintptr(vertexOffsetPos)))
|
||||||
|
gl.TexCoordPointer(2, gl.FLOAT, int32(vertexSize), unsafe.Pointer(uintptr(vertexBuffer)+uintptr(vertexOffsetUv)))
|
||||||
|
gl.ColorPointer(4, gl.UNSIGNED_BYTE, int32(vertexSize), unsafe.Pointer(uintptr(vertexBuffer)+uintptr(vertexOffsetCol)))
|
||||||
|
|
||||||
|
for j := C.int(0); j < commandList.CmdBuffer.Size; j++ {
|
||||||
|
command := (*C.ImDrawCmd)(unsafe.Pointer(uintptr(unsafe.Pointer(commandList.CmdBuffer.Data)) + drawCommandSize*uintptr(j)))
|
||||||
|
gl.Scissor(
|
||||||
|
int32(command.ClipRect.x),
|
||||||
|
int32(c.bufferSize.Y)-int32(command.ClipRect.w),
|
||||||
|
int32(command.ClipRect.z-command.ClipRect.x),
|
||||||
|
int32(command.ClipRect.w-command.ClipRect.y),
|
||||||
|
)
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, uint32(uintptr(command.TextureId)))
|
||||||
|
gl.DrawElements(
|
||||||
|
gl.TRIANGLES,
|
||||||
|
int32(command.ElemCount),
|
||||||
|
gl.UNSIGNED_SHORT,
|
||||||
|
unsafe.Pointer(uintptr(unsafe.Pointer(indexBuffer))+uintptr(elementCount)*uintptr(indexSize)),
|
||||||
|
)
|
||||||
|
|
||||||
|
elementCount += command.ElemCount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gl.DisableClientState(gl.COLOR_ARRAY)
|
||||||
|
gl.DisableClientState(gl.TEXTURE_COORD_ARRAY)
|
||||||
|
gl.DisableClientState(gl.VERTEX_ARRAY)
|
||||||
|
gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture))
|
||||||
|
gl.MatrixMode(gl.MODELVIEW)
|
||||||
|
gl.PopMatrix()
|
||||||
|
gl.MatrixMode(gl.PROJECTION)
|
||||||
|
gl.PopMatrix()
|
||||||
|
gl.PopAttrib()
|
||||||
|
gl.PolygonMode(gl.FRONT, uint32(lastPolygonMode[0]))
|
||||||
|
gl.PolygonMode(gl.BACK, uint32(lastPolygonMode[1]))
|
||||||
|
gl.Viewport(lastViewport[0], lastViewport[1], lastViewport[2], lastViewport[3])
|
||||||
|
gl.Scissor(lastScissorBox[0], lastScissorBox[1], lastScissorBox[2], lastScissorBox[3])
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,28 @@
|
|||||||
package imgui
|
package imgui
|
||||||
|
|
||||||
|
// #include "native.h"
|
||||||
|
import "C"
|
||||||
import (
|
import (
|
||||||
imgui "github.com/FooSoft/imgui-go"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/FooSoft/lazarus/graphics"
|
"github.com/FooSoft/lazarus/graphics"
|
||||||
"github.com/FooSoft/lazarus/math"
|
"github.com/FooSoft/lazarus/math"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (*Context) DialogBegin(label string) bool {
|
func (*Context) DialogBegin(label string) bool {
|
||||||
return imgui.Begin(label)
|
labelC := C.CString(label)
|
||||||
|
defer C.free(unsafe.Pointer(labelC))
|
||||||
|
return bool(C.igBegin(labelC, nil, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Context) DialogEnd() {
|
func (*Context) DialogEnd() {
|
||||||
imgui.End()
|
C.igEnd()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Context) Button(label string) bool {
|
func (*Context) Button(label string) bool {
|
||||||
return imgui.Button(label)
|
labelC := C.CString(label)
|
||||||
|
defer C.free(unsafe.Pointer(labelC))
|
||||||
|
return bool(C.igButton(labelC, C.ImVec2{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Context) Image(texture graphics.Texture) {
|
func (c *Context) Image(texture graphics.Texture) {
|
||||||
@ -23,16 +30,28 @@ func (c *Context) Image(texture graphics.Texture) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (*Context) ImageSized(texture graphics.Texture, size math.Vec2i) {
|
func (*Context) ImageSized(texture graphics.Texture, size math.Vec2i) {
|
||||||
imgui.Image(imgui.TextureID(texture.Id()), imgui.Vec2{X: float32(size.X), Y: float32(size.Y)})
|
C.igImage(
|
||||||
|
C.nativeHandleCast(C.uintptr_t(texture.Id())),
|
||||||
|
C.ImVec2{x: C.float(size.X), y: C.float(size.Y)},
|
||||||
|
C.ImVec2{0, 0},
|
||||||
|
C.ImVec2{1, 1},
|
||||||
|
C.ImVec4{1, 1, 1, 1},
|
||||||
|
C.ImVec4{0, 0, 0, 0},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Context) SliderInt(label string, value *int, min, max int) bool {
|
func (*Context) SliderInt(label string, value *int, min, max int) bool {
|
||||||
temp := int32(*value)
|
labelC := C.CString(label)
|
||||||
result := imgui.SliderInt(label, &temp, int32(min), int32(max))
|
defer C.free(unsafe.Pointer(labelC))
|
||||||
*value = int(temp)
|
valueC := C.int(*value)
|
||||||
|
result := bool(C.igSliderInt(labelC, &valueC, (C.int)(min), (C.int)(max), nil))
|
||||||
|
*value = int(valueC)
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Context) Text(label string) {
|
func (*Context) Text(label string) {
|
||||||
imgui.Text(label)
|
labelStartC := C.CString(label)
|
||||||
|
labelEndC := (*C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(labelStartC)) + uintptr(len(label))))
|
||||||
|
defer C.free(unsafe.Pointer(labelStartC))
|
||||||
|
C.igTextUnformatted(labelStartC, labelEndC)
|
||||||
}
|
}
|
||||||
|
@ -1,44 +0,0 @@
|
|||||||
package imgui
|
|
||||||
|
|
||||||
// #define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
|
|
||||||
// #include "cimgui/cimgui.h"
|
|
||||||
// #cgo linux LDFLAGS: -L./cimgui -l:cimgui.a -lstdc++ -lm
|
|
||||||
import "C"
|
|
||||||
import "unsafe"
|
|
||||||
|
|
||||||
type Context = C.ImGuiContext
|
|
||||||
type DrawData = C.ImDrawData
|
|
||||||
type FontAtlas = C.ImFontAtlas
|
|
||||||
type Io = C.ImGuiIO
|
|
||||||
|
|
||||||
func CreateContext(fontAtlas *FontAtlas) *Context {
|
|
||||||
c := C.igCreateContext(fontAtlas)
|
|
||||||
return c
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) Destroy() {
|
|
||||||
C.igDestroyContext(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFrame() {
|
|
||||||
C.igNewFrame()
|
|
||||||
}
|
|
||||||
|
|
||||||
func Render() {
|
|
||||||
C.igRender()
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDrawData() *DrawData {
|
|
||||||
return C.igGetDrawData()
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetIo() *Io {
|
|
||||||
return C.igGetIO()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (fa *FontAtlas) GetTexDataAsRGBA32() (pixels unsafe.Pointer, width, height int32) {
|
|
||||||
var data *C.uint8_t
|
|
||||||
var w, h C.int
|
|
||||||
C.ImFontAtlas_GetTexDataAsRGBA32(fa, &data, &w, &h, nil)
|
|
||||||
return unsafe.Pointer(data), int32(w), int32(h)
|
|
||||||
}
|
|
@ -1,202 +0,0 @@
|
|||||||
package imgui
|
|
||||||
|
|
||||||
import (
|
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
"github.com/FooSoft/imgui-go"
|
|
||||||
"github.com/go-gl/gl/v2.1/gl"
|
|
||||||
"github.com/veandco/go-sdl2/sdl"
|
|
||||||
)
|
|
||||||
|
|
||||||
var singleton struct {
|
|
||||||
context *imgui.Context
|
|
||||||
refCount int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c *Context) BeginFrame() error {
|
|
||||||
// Setup display size (every frame to accommodate for window resizing)
|
|
||||||
io := imgui.CurrentIO()
|
|
||||||
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 c.lastTime > 0 {
|
|
||||||
io.SetDeltaTime(float32(currentTime-c.lastTime) / float32(frequency))
|
|
||||||
} else {
|
|
||||||
io.SetDeltaTime(1.0 / 60.0)
|
|
||||||
}
|
|
||||||
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, c.buttonsDown[i] || (state&sdl.Button(button)) != 0)
|
|
||||||
c.buttonsDown[i] = false
|
|
||||||
}
|
|
||||||
|
|
||||||
io.SetMousePosition(imgui.Vec2{X: float32(x), Y: float32(y)})
|
|
||||||
|
|
||||||
imgui.NewFrame()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs.
|
|
||||||
// - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application.
|
|
||||||
// - 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 (c *Context) ProcessEvent(event sdl.Event) (bool, error) {
|
|
||||||
switch io := imgui.CurrentIO(); event.GetType() {
|
|
||||||
case sdl.MOUSEWHEEL:
|
|
||||||
wheelEvent := event.(*sdl.MouseWheelEvent)
|
|
||||||
var deltaX, deltaY float32
|
|
||||||
if wheelEvent.X > 0 {
|
|
||||||
deltaX++
|
|
||||||
} else if wheelEvent.X < 0 {
|
|
||||||
deltaX--
|
|
||||||
}
|
|
||||||
if wheelEvent.Y > 0 {
|
|
||||||
deltaY++
|
|
||||||
} else if wheelEvent.Y < 0 {
|
|
||||||
deltaY--
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
case sdl.MOUSEBUTTONDOWN:
|
|
||||||
buttonEvent := event.(*sdl.MouseButtonEvent)
|
|
||||||
switch buttonEvent.Button {
|
|
||||||
case sdl.BUTTON_LEFT:
|
|
||||||
c.buttonsDown[0] = true
|
|
||||||
break
|
|
||||||
case sdl.BUTTON_RIGHT:
|
|
||||||
c.buttonsDown[1] = true
|
|
||||||
break
|
|
||||||
case sdl.BUTTON_MIDDLE:
|
|
||||||
c.buttonsDown[2] = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return true, nil
|
|
||||||
case sdl.TEXTINPUT:
|
|
||||||
inputEvent := event.(*sdl.TextInputEvent)
|
|
||||||
io.AddInputCharacters(string(inputEvent.Text[:]))
|
|
||||||
return true, nil
|
|
||||||
case sdl.KEYDOWN:
|
|
||||||
keyEvent := event.(*sdl.KeyboardEvent)
|
|
||||||
io.KeyPress(int(keyEvent.Keysym.Scancode))
|
|
||||||
modState := int(sdl.GetModState())
|
|
||||||
io.KeyShift(modState&sdl.KMOD_LSHIFT, modState&sdl.KMOD_RSHIFT)
|
|
||||||
io.KeyCtrl(modState&sdl.KMOD_LCTRL, modState&sdl.KMOD_RCTRL)
|
|
||||||
io.KeyAlt(modState&sdl.KMOD_LALT, modState&sdl.KMOD_RALT)
|
|
||||||
case sdl.KEYUP:
|
|
||||||
keyEvent := event.(*sdl.KeyboardEvent)
|
|
||||||
io.KeyRelease(int(keyEvent.Keysym.Scancode))
|
|
||||||
modState := int(sdl.GetModState())
|
|
||||||
io.KeyShift(modState&sdl.KMOD_LSHIFT, modState&sdl.KMOD_RSHIFT)
|
|
||||||
io.KeyCtrl(modState&sdl.KMOD_LCTRL, modState&sdl.KMOD_RCTRL)
|
|
||||||
io.KeyAlt(modState&sdl.KMOD_LALT, modState&sdl.KMOD_RALT)
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 (c *Context) EndFrame() error {
|
|
||||||
imgui.Render()
|
|
||||||
drawData := imgui.RenderedDrawData()
|
|
||||||
drawData.ScaleClipRects(imgui.Vec2{
|
|
||||||
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!
|
|
||||||
// Setup render singleton: alpha-blending enabled, no face culling, no depth testing, scissor enabled, vertex/texcoord/color pointers, polygon fill.
|
|
||||||
var lastTexture int32
|
|
||||||
gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
|
|
||||||
var lastPolygonMode [2]int32
|
|
||||||
gl.GetIntegerv(gl.POLYGON_MODE, &lastPolygonMode[0])
|
|
||||||
var lastViewport [4]int32
|
|
||||||
gl.GetIntegerv(gl.VIEWPORT, &lastViewport[0])
|
|
||||||
var lastScissorBox [4]int32
|
|
||||||
gl.GetIntegerv(gl.SCISSOR_BOX, &lastScissorBox[0])
|
|
||||||
gl.PushAttrib(gl.ENABLE_BIT | gl.COLOR_BUFFER_BIT | gl.TRANSFORM_BIT)
|
|
||||||
gl.Enable(gl.BLEND)
|
|
||||||
gl.BlendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
|
|
||||||
gl.Disable(gl.CULL_FACE)
|
|
||||||
gl.Disable(gl.DEPTH_TEST)
|
|
||||||
gl.Disable(gl.LIGHTING)
|
|
||||||
gl.Disable(gl.COLOR_MATERIAL)
|
|
||||||
gl.Enable(gl.SCISSOR_TEST)
|
|
||||||
gl.EnableClientState(gl.VERTEX_ARRAY)
|
|
||||||
gl.EnableClientState(gl.TEXTURE_COORD_ARRAY)
|
|
||||||
gl.EnableClientState(gl.COLOR_ARRAY)
|
|
||||||
gl.Enable(gl.TEXTURE_2D)
|
|
||||||
gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL)
|
|
||||||
|
|
||||||
// You may want this if using this code in an OpenGL 3+ context where shaders may be bound
|
|
||||||
// gl.UseProgram(0)
|
|
||||||
|
|
||||||
// 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(c.bufferSize.X), int32(c.bufferSize.Y))
|
|
||||||
gl.MatrixMode(gl.PROJECTION)
|
|
||||||
gl.PushMatrix()
|
|
||||||
gl.LoadIdentity()
|
|
||||||
gl.Ortho(0, float64(c.displaySize.X), float64(c.displaySize.Y), 0, -1, 1)
|
|
||||||
gl.MatrixMode(gl.MODELVIEW)
|
|
||||||
gl.PushMatrix()
|
|
||||||
gl.LoadIdentity()
|
|
||||||
|
|
||||||
vertexSize, vertexOffsetPos, vertexOffsetUv, vertexOffsetCol := imgui.VertexBufferLayout()
|
|
||||||
indexSize := imgui.IndexBufferLayout()
|
|
||||||
|
|
||||||
drawType := gl.UNSIGNED_SHORT
|
|
||||||
if indexSize == 4 {
|
|
||||||
drawType = gl.UNSIGNED_INT
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render command lists
|
|
||||||
for _, commandList := range drawData.CommandLists() {
|
|
||||||
vertexBuffer, _ := commandList.VertexBuffer()
|
|
||||||
indexBuffer, _ := commandList.IndexBuffer()
|
|
||||||
indexBufferOffset := uintptr(indexBuffer)
|
|
||||||
|
|
||||||
gl.VertexPointer(2, gl.FLOAT, int32(vertexSize), unsafe.Pointer(uintptr(vertexBuffer)+uintptr(vertexOffsetPos)))
|
|
||||||
gl.TexCoordPointer(2, gl.FLOAT, int32(vertexSize), unsafe.Pointer(uintptr(vertexBuffer)+uintptr(vertexOffsetUv)))
|
|
||||||
gl.ColorPointer(4, gl.UNSIGNED_BYTE, int32(vertexSize), unsafe.Pointer(uintptr(vertexBuffer)+uintptr(vertexOffsetCol)))
|
|
||||||
|
|
||||||
for _, command := range commandList.Commands() {
|
|
||||||
if command.HasUserCallback() {
|
|
||||||
command.CallUserCallback(commandList)
|
|
||||||
} else {
|
|
||||||
clipRect := command.ClipRect()
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
indexBufferOffset += uintptr(command.ElementCount() * indexSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restore modified state
|
|
||||||
gl.DisableClientState(gl.COLOR_ARRAY)
|
|
||||||
gl.DisableClientState(gl.TEXTURE_COORD_ARRAY)
|
|
||||||
gl.DisableClientState(gl.VERTEX_ARRAY)
|
|
||||||
gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture))
|
|
||||||
gl.MatrixMode(gl.MODELVIEW)
|
|
||||||
gl.PopMatrix()
|
|
||||||
gl.MatrixMode(gl.PROJECTION)
|
|
||||||
gl.PopMatrix()
|
|
||||||
gl.PopAttrib()
|
|
||||||
gl.PolygonMode(gl.FRONT, uint32(lastPolygonMode[0]))
|
|
||||||
gl.PolygonMode(gl.BACK, uint32(lastPolygonMode[1]))
|
|
||||||
gl.Viewport(lastViewport[0], lastViewport[1], lastViewport[2], lastViewport[3])
|
|
||||||
gl.Scissor(lastScissorBox[0], lastScissorBox[1], lastScissorBox[2], lastScissorBox[3])
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
10
platform/imgui/native.h
Normal file
10
platform/imgui/native.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define CIMGUI_DEFINE_ENUMS_AND_STRUCTS
|
||||||
|
#define IM_OFFSETOF(_TYPE,_MEMBER) ((size_t)&(((_TYPE*)0)->_MEMBER))
|
||||||
|
#include "cimgui/cimgui.h"
|
||||||
|
|
||||||
|
// silly trick to get go-vet off our back
|
||||||
|
inline ImTextureID nativeHandleCast(uintptr_t id) {
|
||||||
|
return (ImTextureID)id;
|
||||||
|
}
|
@ -46,7 +46,11 @@ func (s *scene) Name() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *scene) Destroy(window *platform.Window) error {
|
func (s *scene) Destroy(window *platform.Window) error {
|
||||||
|
if s.texture != nil {
|
||||||
return s.texture.Destroy()
|
return s.texture.Destroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *scene) Advance(window *platform.Window) error {
|
func (s *scene) Advance(window *platform.Window) error {
|
||||||
|
Loading…
Reference in New Issue
Block a user