lazarus/platform/imgui/impl.go

203 lines
7.3 KiB
Go
Raw Normal View History

package imgui_backend
2018-12-27 22:35:06 +00:00
import (
"unsafe"
"github.com/FooSoft/imgui-go"
"github.com/go-gl/gl/v2.1/gl"
"github.com/veandco/go-sdl2/sdl"
)
var singleton struct {
2018-12-31 21:10:58 +00:00
context *imgui.Context
refCount int
}
2018-12-31 21:10:58 +00:00
func (c *Context) BeginFrame() error {
2018-12-27 22:35:06 +00:00
// Setup display size (every frame to accommodate for window resizing)
io := imgui.CurrentIO()
2018-12-31 21:10:58 +00:00
io.SetDisplaySize(imgui.Vec2{
X: float32(c.displaySize.X),
Y: float32(c.displaySize.Y),
})
2018-12-27 22:35:06 +00:00
// Setup time step (we don't use SDL_GetTicks() because it is using millisecond resolution)
frequency := sdl.GetPerformanceFrequency()
currentTime := sdl.GetPerformanceCounter()
2018-12-31 21:10:58 +00:00
if c.lastTime > 0 {
io.SetDeltaTime(float32(currentTime-c.lastTime) / float32(frequency))
2018-12-27 22:35:06 +00:00
} else {
io.SetDeltaTime(1.0 / 60.0)
}
2018-12-31 21:10:58 +00:00
c.lastTime = currentTime
2018-12-27 22:35:06 +00:00
// 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} {
2018-12-31 21:10:58 +00:00
io.SetMouseButtonDown(i, c.buttonsDown[i] || (state&sdl.Button(button)) != 0)
c.buttonsDown[i] = false
2018-12-27 22:35:06 +00:00
}
io.SetMousePosition(imgui.Vec2{X: float32(x), Y: float32(y)})
imgui.NewFrame()
return nil
2018-12-27 22:35:06 +00:00
}
// 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.
2018-12-31 21:10:58 +00:00
func (c *Context) ProcessEvent(event sdl.Event) (bool, error) {
2018-12-27 22:35:06 +00:00
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
2018-12-27 22:35:06 +00:00
case sdl.MOUSEBUTTONDOWN:
buttonEvent := event.(*sdl.MouseButtonEvent)
switch buttonEvent.Button {
case sdl.BUTTON_LEFT:
2018-12-31 21:10:58 +00:00
c.buttonsDown[0] = true
2018-12-27 22:35:06 +00:00
break
case sdl.BUTTON_RIGHT:
2018-12-31 21:10:58 +00:00
c.buttonsDown[1] = true
2018-12-27 22:35:06 +00:00
break
case sdl.BUTTON_MIDDLE:
2018-12-31 21:10:58 +00:00
c.buttonsDown[2] = true
2018-12-27 22:35:06 +00:00
break
}
return true, nil
2018-12-27 22:35:06 +00:00
case sdl.TEXTINPUT:
inputEvent := event.(*sdl.TextInputEvent)
io.AddInputCharacters(string(inputEvent.Text[:]))
return true, nil
2018-12-27 22:35:06 +00:00
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
2018-12-27 22:35:06 +00:00
}
return false, nil
2018-12-27 22:35:06 +00:00
}
// 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.
2018-12-31 21:10:58 +00:00
func (c *Context) EndFrame() error {
2018-12-31 19:46:12 +00:00
imgui.Render()
drawData := imgui.RenderedDrawData()
drawData.ScaleClipRects(imgui.Vec2{
2018-12-31 21:10:58 +00:00
X: float32(c.bufferSize.X) / float32(c.displaySize.X),
Y: float32(c.bufferSize.Y) / float32(c.displaySize.Y),
})
2018-12-27 22:35:06 +00:00
// 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.
2018-12-27 22:35:06 +00:00
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.
2018-12-31 21:10:58 +00:00
gl.Viewport(0, 0, int32(c.bufferSize.X), int32(c.bufferSize.Y))
2018-12-27 22:35:06 +00:00
gl.MatrixMode(gl.PROJECTION)
gl.PushMatrix()
gl.LoadIdentity()
2018-12-31 21:10:58 +00:00
gl.Ortho(0, float64(c.displaySize.X), float64(c.displaySize.Y), 0, -1, 1)
2018-12-27 22:35:06 +00:00
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()
2018-12-31 21:10:58 +00:00
gl.Scissor(int32(clipRect.X), int32(c.bufferSize.Y)-int32(clipRect.W), int32(clipRect.Z-clipRect.X), int32(clipRect.W-clipRect.Y))
2018-12-27 22:35:06 +00:00
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
2018-12-27 22:35:06 +00:00
}