lazarus/platform/imgui/context.go

293 lines
9.0 KiB
Go
Raw Permalink Normal View History

2019-01-01 01:30:59 +00:00
package imgui
2018-12-31 21:10:58 +00:00
2019-01-04 03:05:19 +00:00
// #include "native.h"
import "C"
2018-12-31 21:10:58 +00:00
import (
"errors"
2019-01-01 01:10:45 +00:00
"log"
2019-01-04 03:05:19 +00:00
"unsafe"
2019-01-01 01:10:45 +00:00
2018-12-31 21:10:58 +00:00
"github.com/FooSoft/lazarus/math"
"github.com/go-gl/gl/v2.1/gl"
"github.com/veandco/go-sdl2/sdl"
)
var (
ErrNotInit = errors.New("imgui context was not created")
)
2019-01-04 03:05:19 +00:00
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 imguiState struct {
2019-01-10 03:09:27 +00:00
context *C.ImGuiContext
io *C.ImGuiIO
2019-01-04 03:05:19 +00:00
2018-12-31 21:10:58 +00:00
fontTexture uint32
displaySize math.Vec2i
bufferSize math.Vec2i
buttonsDown [3]bool
lastTime uint64
2018-12-31 21:10:58 +00:00
}
func Create() error {
if IsCreated() {
return nil
2018-12-31 21:10:58 +00:00
}
log.Println("imgui create")
2019-01-10 03:09:27 +00:00
imguiState.context = C.igCreateContext(nil)
imguiState.io = C.igGetIO()
imguiState.io.IniFilename = nil
for imguiKey, nativeKey := range keyMapping {
2019-01-10 03:09:27 +00:00
imguiState.io.KeyMap[imguiKey] = nativeKey
}
2018-12-31 21:10:58 +00:00
2019-01-04 03:05:19 +00:00
var imageData *C.uchar
var imageWidth, imageHeight C.int
2019-01-10 03:09:27 +00:00
C.ImFontAtlas_GetTexDataAsRGBA32(imguiState.io.Fonts, &imageData, &imageWidth, &imageHeight, nil)
2018-12-31 21:10:58 +00:00
var lastTexture int32
gl.GetIntegerv(gl.TEXTURE_BINDING_2D, &lastTexture)
gl.GenTextures(1, &imguiState.fontTexture)
gl.BindTexture(gl.TEXTURE_2D, imguiState.fontTexture)
2018-12-31 21:10:58 +00:00
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)
2019-01-04 03:05:19 +00:00
gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, int32(imageWidth), int32(imageHeight), 0, gl.RGBA, gl.UNSIGNED_BYTE, unsafe.Pointer(imageData))
2018-12-31 21:10:58 +00:00
gl.BindTexture(gl.TEXTURE_2D, uint32(lastTexture))
return nil
2018-12-31 21:10:58 +00:00
}
func IsCreated() bool {
2019-01-10 03:09:27 +00:00
return imguiState.context != nil
}
func Destroy() error {
if !IsCreated() {
2018-12-31 21:10:58 +00:00
return nil
}
2019-01-10 03:09:27 +00:00
log.Println("imgui destroy")
gl.DeleteTextures(1, &imguiState.fontTexture)
2019-01-10 03:09:27 +00:00
imguiState.io.Fonts.TexID = C.nativeHandleCast(C.uintptr_t(imguiState.fontTexture))
imguiState.fontTexture = 0
2019-01-10 03:09:27 +00:00
C.igDestroyContext(imguiState.context)
imguiState.context = nil
imguiState.io = nil
2018-12-31 21:10:58 +00:00
return nil
}
2019-01-04 03:05:19 +00:00
func BeginFrame(displaySize, bufferSize math.Vec2i) error {
if !IsCreated() {
return ErrNotInit
}
2019-01-04 03:05:19 +00:00
imguiState.displaySize = displaySize
imguiState.bufferSize = bufferSize
2019-01-04 03:05:19 +00:00
2019-01-10 03:09:27 +00:00
imguiState.io.Fonts.TexID = C.nativeHandleCast(C.uintptr_t(imguiState.fontTexture))
imguiState.io.DisplaySize.x = C.float(displaySize.X)
imguiState.io.DisplaySize.y = C.float(displaySize.Y)
2019-01-04 03:05:19 +00:00
currentTime := sdl.GetPerformanceCounter()
if imguiState.lastTime > 0 {
2019-01-10 03:09:27 +00:00
imguiState.io.DeltaTime = C.float(float32(currentTime-imguiState.lastTime) / float32(sdl.GetPerformanceFrequency()))
2019-01-04 03:05:19 +00:00
} else {
2019-01-10 03:09:27 +00:00
imguiState.io.DeltaTime = C.float(1.0 / 60.0)
2019-01-04 03:05:19 +00:00
}
imguiState.lastTime = currentTime
2019-01-04 03:05:19 +00:00
x, y, state := sdl.GetMouseState()
2019-01-10 03:09:27 +00:00
imguiState.io.MousePos.x = C.float(x)
imguiState.io.MousePos.y = C.float(y)
2019-01-04 03:05:19 +00:00
for i, button := range []uint32{sdl.BUTTON_LEFT, sdl.BUTTON_RIGHT, sdl.BUTTON_MIDDLE} {
2019-01-10 03:09:27 +00:00
imguiState.io.MouseDown[i] = C.bool(imguiState.buttonsDown[i] || (state&sdl.Button(button)) != 0)
imguiState.buttonsDown[i] = false
2019-01-04 03:05:19 +00:00
}
C.igNewFrame()
return nil
2019-01-04 03:05:19 +00:00
}
func ProcessEvent(event sdl.Event) (bool, error) {
if !IsCreated() {
return false, ErrNotInit
}
2019-01-04 03:05:19 +00:00
switch event.GetType() {
case sdl.MOUSEWHEEL:
wheelEvent := event.(*sdl.MouseWheelEvent)
2019-01-10 03:09:27 +00:00
imguiState.io.MouseWheelH += C.float(wheelEvent.X)
imguiState.io.MouseWheel += C.float(wheelEvent.Y)
2019-01-04 03:05:19 +00:00
return true, nil
case sdl.MOUSEBUTTONDOWN:
buttonEvent := event.(*sdl.MouseButtonEvent)
switch buttonEvent.Button {
case sdl.BUTTON_LEFT:
imguiState.buttonsDown[0] = true
2019-01-04 03:05:19 +00:00
break
case sdl.BUTTON_RIGHT:
imguiState.buttonsDown[1] = true
2019-01-04 03:05:19 +00:00
break
case sdl.BUTTON_MIDDLE:
imguiState.buttonsDown[2] = true
2019-01-04 03:05:19 +00:00
break
}
return true, nil
case sdl.TEXTINPUT:
inputEvent := event.(*sdl.TextInputEvent)
2019-01-10 03:09:27 +00:00
C.ImGuiIO_AddInputCharactersUTF8(imguiState.io, (*C.char)(unsafe.Pointer(&inputEvent.Text[0])))
2019-01-04 03:05:19 +00:00
return true, nil
case sdl.KEYDOWN:
keyEvent := event.(*sdl.KeyboardEvent)
2019-01-10 03:09:27 +00:00
imguiState.io.KeysDown[keyEvent.Keysym.Scancode] = true
2019-01-04 03:05:19 +00:00
modState := sdl.GetModState()
2019-01-10 03:09:27 +00:00
imguiState.io.KeyCtrl = C.bool(modState&sdl.KMOD_CTRL != 0)
imguiState.io.KeyAlt = C.bool(modState&sdl.KMOD_ALT != 0)
imguiState.io.KeyShift = C.bool(modState&sdl.KMOD_SHIFT != 0)
2019-01-04 03:05:19 +00:00
case sdl.KEYUP:
keyEvent := event.(*sdl.KeyboardEvent)
2019-01-10 03:09:27 +00:00
imguiState.io.KeysDown[keyEvent.Keysym.Scancode] = false
2019-01-04 03:05:19 +00:00
modState := sdl.GetModState()
2019-01-10 03:09:27 +00:00
imguiState.io.KeyCtrl = C.bool(modState&sdl.KMOD_CTRL != 0)
imguiState.io.KeyAlt = C.bool(modState&sdl.KMOD_ALT != 0)
imguiState.io.KeyShift = C.bool(modState&sdl.KMOD_SHIFT != 0)
2019-01-04 03:05:19 +00:00
return true, nil
}
return false, nil
}
func EndFrame() error {
if !IsCreated() {
return ErrNotInit
}
2019-01-04 03:05:19 +00:00
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(imguiState.bufferSize.X), int32(imguiState.bufferSize.Y))
2019-01-04 03:05:19 +00:00
gl.MatrixMode(gl.PROJECTION)
gl.PushMatrix()
gl.LoadIdentity()
gl.Ortho(0, float64(imguiState.displaySize.X), float64(imguiState.displaySize.Y), 0, -1, 1)
2019-01-04 03:05:19 +00:00
gl.MatrixMode(gl.MODELVIEW)
gl.PushMatrix()
gl.LoadIdentity()
drawData := C.igGetDrawData()
C.ImDrawData_ScaleClipRects(
drawData,
C.ImVec2{
x: C.float(float32(imguiState.bufferSize.X) / float32(imguiState.displaySize.X)),
y: C.float(float32(imguiState.bufferSize.Y) / float32(imguiState.displaySize.Y)),
2019-01-04 03:05:19 +00:00
},
)
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(imguiState.bufferSize.Y)-int32(command.ClipRect.w),
2019-01-04 03:05:19 +00:00
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
}