From ad7067101a3ce40f350e125a1170d61617d9f4a3 Mon Sep 17 00:00:00 2001 From: Alex Yatskov Date: Thu, 3 Jan 2019 19:05:19 -0800 Subject: [PATCH] switch imgui backend to cimgui --- .gitmodules | 2 +- platform/imgui/{imgui_wrapper => }/cimgui | 0 platform/imgui/context.go | 299 ++++++++++++++---- platform/imgui/imgui.go | 37 ++- platform/imgui/imgui_wrapper/imgui_wrapper.go | 44 --- platform/imgui/impl.go | 202 ------------ platform/imgui/native.h | 10 + tools/viewer/main.go | 6 +- 8 files changed, 279 insertions(+), 321 deletions(-) rename platform/imgui/{imgui_wrapper => }/cimgui (100%) delete mode 100644 platform/imgui/imgui_wrapper/imgui_wrapper.go delete mode 100644 platform/imgui/impl.go create mode 100644 platform/imgui/native.h diff --git a/.gitmodules b/.gitmodules index 26256ca..b8fa7af 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,5 +2,5 @@ path = formats/mpq/stormlib url = https://github.com/ladislav-zezula/StormLib.git [submodule "platform/imgui/cimgui"] - path = platform/imgui/imgui_wrapper/cimgui + path = platform/imgui/cimgui url = https://github.com/cimgui/cimgui.git diff --git a/platform/imgui/imgui_wrapper/cimgui b/platform/imgui/cimgui similarity index 100% rename from platform/imgui/imgui_wrapper/cimgui rename to platform/imgui/cimgui diff --git a/platform/imgui/context.go b/platform/imgui/context.go index f3afa43..5846607 100644 --- a/platform/imgui/context.go +++ b/platform/imgui/context.go @@ -1,14 +1,58 @@ package imgui +// #cgo linux CFLAGS: -I./cimgui +// #cgo linux LDFLAGS: -L./cimgui -l:cimgui.a -lstdc++ -lm +// #include "native.h" +import "C" import ( "log" + "unsafe" - 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" ) +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 { buttonsDown [3]bool lastTime uint64 @@ -21,77 +65,55 @@ func New(displaySize, bufferSize math.Vec2i) (*Context, error) { singleton.refCount++ if singleton.refCount == 1 { log.Println("imgui global create") - singleton.context = imgui.CreateContext(nil) + singleton.nativeContext = C.igCreateContext(nil) + singleton.nativeIo = C.igGetIO() - 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) + for imguiKey, nativeKey := range keyMapping { + singleton.nativeIo.KeyMap[imguiKey] = nativeKey } } log.Println("imgui context create") c := &Context{displaySize: displaySize, bufferSize: bufferSize} - // Build texture atlas - io := imgui.CurrentIO() - image := io.Fonts().TextureDataRGBA32() + var imageData *C.uchar + var imageWidth, imageHeight C.int + C.ImFontAtlas_GetTexDataAsRGBA32(singleton.nativeIo.Fonts, &imageData, &imageWidth, &imageHeight, nil) - // 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.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)) - // Store texture identifier - io.Fonts().SetTextureID(imgui.TextureID(c.fontTexture)) - 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) { c.displaySize = displaySize } @@ -100,22 +122,171 @@ func (c *Context) SetBufferSize(bufferSize math.Vec2i) { c.bufferSize = bufferSize } -func (c *Context) Destroy() error { - if c == nil || c.fontTexture == 0 { - return nil +func (c *Context) BeginFrame() { + singleton.nativeIo.Fonts.TexID = C.nativeHandleCast(C.uintptr_t(c.fontTexture)) + 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") - gl.DeleteTextures(1, &c.fontTexture) - imgui.CurrentIO().Fonts().SetTextureID(0) - c.fontTexture = 0 + C.igNewFrame() +} - singleton.refCount-- - if singleton.refCount == 0 { - log.Println("imgui global destroy") - singleton.context.Destroy() - singleton.context = nil +func (c *Context) ProcessEvent(event sdl.Event) (bool, error) { + switch event.GetType() { + case sdl.MOUSEWHEEL: + wheelEvent := event.(*sdl.MouseWheelEvent) + 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 } diff --git a/platform/imgui/imgui.go b/platform/imgui/imgui.go index 4adadb2..88f9686 100644 --- a/platform/imgui/imgui.go +++ b/platform/imgui/imgui.go @@ -1,21 +1,28 @@ package imgui +// #include "native.h" +import "C" import ( - imgui "github.com/FooSoft/imgui-go" + "unsafe" + "github.com/FooSoft/lazarus/graphics" "github.com/FooSoft/lazarus/math" ) 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() { - imgui.End() + C.igEnd() } 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) { @@ -23,16 +30,28 @@ func (c *Context) Image(texture graphics.Texture) { } 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 { - temp := int32(*value) - result := imgui.SliderInt(label, &temp, int32(min), int32(max)) - *value = int(temp) + labelC := C.CString(label) + defer C.free(unsafe.Pointer(labelC)) + valueC := C.int(*value) + result := bool(C.igSliderInt(labelC, &valueC, (C.int)(min), (C.int)(max), nil)) + *value = int(valueC) return result } 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) } diff --git a/platform/imgui/imgui_wrapper/imgui_wrapper.go b/platform/imgui/imgui_wrapper/imgui_wrapper.go deleted file mode 100644 index 19a728c..0000000 --- a/platform/imgui/imgui_wrapper/imgui_wrapper.go +++ /dev/null @@ -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) -} diff --git a/platform/imgui/impl.go b/platform/imgui/impl.go deleted file mode 100644 index 9b8dd25..0000000 --- a/platform/imgui/impl.go +++ /dev/null @@ -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 -} diff --git a/platform/imgui/native.h b/platform/imgui/native.h new file mode 100644 index 0000000..e775188 --- /dev/null +++ b/platform/imgui/native.h @@ -0,0 +1,10 @@ +#include + +#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; +} diff --git a/tools/viewer/main.go b/tools/viewer/main.go index f1ed7d0..1617832 100644 --- a/tools/viewer/main.go +++ b/tools/viewer/main.go @@ -46,7 +46,11 @@ func (s *scene) Name() string { } func (s *scene) Destroy(window *platform.Window) error { - return s.texture.Destroy() + if s.texture != nil { + return s.texture.Destroy() + } + + return nil } func (s *scene) Advance(window *platform.Window) error {