Skip to content

Instantly share code, notes, and snippets.

@lucaspoffo
Created February 4, 2026 16:17
Show Gist options
  • Select an option

  • Save lucaspoffo/2dd681d1f765c4f72a9396eaf9d227a2 to your computer and use it in GitHub Desktop.

Select an option

Save lucaspoffo/2dd681d1f765c4f72a9396eaf9d227a2 to your computer and use it in GitHub Desktop.
Dear Imgui backend implementation for Karl2D
package imgui_impl_karl2d
// Based on the raylib extras rlImGui: https://github.com/raylib-extras/rlImGui/blob/main/rlImGui.cpp
/* Usage:
import im_k2 "imgui_impl_karl2d"
import im "../../odin-imgui"
main :: proc() {
k2.init(1280, 720, "karl2D Imgui")
im.CreateContext()
defer im.DestroyContext()
im_k2.init()
defer im_k2.shutdown()
for k2.update() {
k2.clear(k2.LIGHT_BLUE)
im_k2.process_events()
im_k2.new_frame()
im.NewFrame()
im.ShowDemoWindow()
im.Render()
im_k2.render_draw_data(im.GetDrawData())
k2.present()
}
k2.shutdown()
}
*/
import "core:c"
import "core:mem"
import "core:math"
import "core:slice"
// Follow build instruction for imgui bindings in: https://gitlab.com/L-4/odin-imgui
import imgui ".."
import k2 "../../karl2d"
karl2d_key_map: #sparse [k2.Keyboard_Key]imgui.Key = {
.None = imgui.Key.None,
.Backtick = imgui.Key.GraveAccent,
.Apostrophe = imgui.Key.Apostrophe,
.Menu = imgui.Key.Menu,
.Comma = imgui.Key.Comma,
.Minus = imgui.Key.Minus,
.Period = imgui.Key.Period,
.Slash = imgui.Key.Slash,
.N0 = imgui.Key._0,
.N1 = imgui.Key._1,
.N2 = imgui.Key._2,
.N3 = imgui.Key._3,
.N4 = imgui.Key._4,
.N5 = imgui.Key._5,
.N6 = imgui.Key._6,
.N7 = imgui.Key._7,
.N8 = imgui.Key._8,
.N9 = imgui.Key._9,
.Semicolon = imgui.Key.Semicolon,
.Equal = imgui.Key.Equal,
.A = imgui.Key.A,
.B = imgui.Key.B,
.C = imgui.Key.C,
.D = imgui.Key.D,
.E = imgui.Key.E,
.F = imgui.Key.F,
.G = imgui.Key.G,
.H = imgui.Key.H,
.I = imgui.Key.I,
.J = imgui.Key.J,
.K = imgui.Key.K,
.L = imgui.Key.L,
.M = imgui.Key.M,
.N = imgui.Key.N,
.O = imgui.Key.O,
.P = imgui.Key.P,
.Q = imgui.Key.Q,
.R = imgui.Key.R,
.S = imgui.Key.S,
.T = imgui.Key.T,
.U = imgui.Key.U,
.V = imgui.Key.V,
.W = imgui.Key.W,
.X = imgui.Key.X,
.Y = imgui.Key.Y,
.Z = imgui.Key.Z,
.Space = imgui.Key.Space,
.Escape = imgui.Key.Escape,
.Enter = imgui.Key.Enter,
.Tab = imgui.Key.Tab,
.Backspace = imgui.Key.Backspace,
.Insert = imgui.Key.Insert,
.Delete = imgui.Key.Delete,
.Right = imgui.Key.RightArrow,
.Left = imgui.Key.LeftArrow,
.Down = imgui.Key.DownArrow,
.Up = imgui.Key.UpArrow,
.Page_Up = imgui.Key.PageUp,
.Page_Down = imgui.Key.PageDown,
.Home = imgui.Key.Home,
.End = imgui.Key.End,
.Caps_Lock = imgui.Key.CapsLock,
.Scroll_Lock = imgui.Key.ScrollLock,
.Num_Lock = imgui.Key.NumLock,
.Print_Screen = imgui.Key.PrintScreen,
.Pause = imgui.Key.Pause,
.F1 = imgui.Key.F1,
.F2 = imgui.Key.F2,
.F3 = imgui.Key.F3,
.F4 = imgui.Key.F4,
.F5 = imgui.Key.F5,
.F6 = imgui.Key.F6,
.F7 = imgui.Key.F7,
.F8 = imgui.Key.F8,
.F9 = imgui.Key.F9,
.F10 = imgui.Key.F10,
.F11 = imgui.Key.F11,
.F12 = imgui.Key.F12,
.Left_Shift = imgui.Key.LeftShift,
.Left_Control = imgui.Key.LeftCtrl,
.Left_Alt = imgui.Key.LeftAlt,
.Left_Super = imgui.Key.LeftSuper,
.Right_Shift = imgui.Key.RightShift,
.Right_Control = imgui.Key.RightCtrl,
.Right_Alt = imgui.Key.RightAlt,
.Right_Super = imgui.Key.RightSuper,
.Left_Bracket = imgui.Key.LeftBracket,
.Backslash = imgui.Key.Backslash,
.Right_Bracket = imgui.Key.RightBracket,
.NP_0 = imgui.Key.Keypad0,
.NP_1 = imgui.Key.Keypad1,
.NP_2 = imgui.Key.Keypad2,
.NP_3 = imgui.Key.Keypad3,
.NP_4 = imgui.Key.Keypad4,
.NP_5 = imgui.Key.Keypad5,
.NP_6 = imgui.Key.Keypad6,
.NP_7 = imgui.Key.Keypad7,
.NP_8 = imgui.Key.Keypad8,
.NP_9 = imgui.Key.Keypad9,
.NP_Decimal = imgui.Key.KeypadDecimal,
.NP_Divide = imgui.Key.KeypadDivide,
.NP_Multiply = imgui.Key.KeypadMultiply,
.NP_Subtract = imgui.Key.KeypadSubtract,
.NP_Add = imgui.Key.KeypadAdd,
.NP_Enter = imgui.Key.KeypadEnter,
.NP_Equal = imgui.Key.KeypadEqual,
}
init :: proc() {
io: ^imgui.IO = imgui.GetIO()
io.BackendPlatformName = "imgui_impl_karl2d"
io.BackendFlags = { .RendererHasTextures }
io.MousePos = { 0, 0 }
}
shutdown :: proc() {
io: ^imgui.PlatformIO = imgui.GetPlatformIO()
textures: []^imgui.TextureData = mem.slice_ptr(io.Textures.Data, int(io.Textures.Size))
for texture in textures {
if texture.Status != .Destroyed {
handle := transmute(k2.Texture_Handle)texture.TexID
texture := k2.Texture { handle = handle }
k2.destroy_texture(texture)
}
}
}
new_frame :: proc() {
io: ^imgui.IO = imgui.GetIO()
io.DisplaySize.x = f32(k2.get_screen_width())
io.DisplaySize.y = f32(k2.get_screen_height())
io.DisplayFramebufferScale = k2.get_window_scale()
io.DeltaTime = k2.get_frame_time()
// if io.WantSetMousePos {
// k2.set_mouse_position(io.MousePos)
// }
}
render_draw_data :: proc(draw_data: ^imgui.DrawData) {
k2.draw_current_batch()
if draw_data.Textures != nil {
textures: []^imgui.TextureData = mem.slice_ptr(draw_data.Textures.Data, int(draw_data.Textures.Size))
for tex in textures {
update_texture(tex)
}
}
command_lists: []^imgui.DrawList = mem.slice_ptr(draw_data.CmdLists.Data, int(draw_data.CmdLists.Size))
for command_list in command_lists {
cmd_slice: []imgui.DrawCmd = mem.slice_ptr(command_list.CmdBuffer.Data, int(command_list.CmdBuffer.Size))
for i in 0..<command_list.CmdBuffer.Size {
cmd := &cmd_slice[i]
k2.set_scissor_rect(k2.Rect {
cmd.ClipRect.x - draw_data.DisplayPos.x,
cmd.ClipRect.y,
cmd.ClipRect.z - (cmd.ClipRect.x - draw_data.DisplayPos.x),
cmd.ClipRect.w - (cmd.ClipRect.y - draw_data.DisplayPos.y)
})
if cmd.UserCallback != nil {
cmd.UserCallback(command_list, cmd)
continue
}
tex_id := imgui.DrawCmd_GetTexID(cmd)
render_triangles(cmd.ElemCount, cmd.IdxOffset, command_list.IdxBuffer, command_list.VtxBuffer, tex_id)
k2.draw_current_batch()
}
}
k2.set_scissor_rect(nil)
}
@private
render_triangles :: proc(count: u32, index_start: u32, index_buffer: imgui.Vector_DrawIdx, vert_buffer: imgui.Vector_DrawVert, texture_ptr: imgui.TextureID) {
if count < 3 {
return
}
texture: k2.Texture_Handle = transmute(k2.Texture_Handle)texture_ptr
k2.set_texture(texture)
index_slice: []imgui.DrawIdx = mem.slice_ptr(index_buffer.Data, int(index_buffer.Size))
vert_slice: []imgui.DrawVert = mem.slice_ptr(vert_buffer.Data, int(vert_buffer.Size))
for i: u32 = 0; i <= (count - 3); i += 3 {
index_a := index_slice[index_start + i]
index_b := index_slice[index_start + i + 1]
index_c := index_slice[index_start + i + 2]
vertex_a := vert_slice[index_a]
vertex_b := vert_slice[index_b]
vertex_c := vert_slice[index_c]
k2.batch_vertex(vertex_a.pos, vertex_a.uv, transmute(k2.Color)vertex_a.col)
k2.batch_vertex(vertex_b.pos, vertex_b.uv, transmute(k2.Color)vertex_b.col)
k2.batch_vertex(vertex_c.pos, vertex_c.uv, transmute(k2.Color)vertex_c.col)
}
}
process_events :: proc() {
io: ^imgui.IO = imgui.GetIO()
for event in k2.get_events() {
#partial switch &e in event {
case k2.Event_Key_Went_Down:
imgui.IO_AddKeyEvent(io, karl2d_key_map[e.key], true)
key := u32(e.key)
if key < 255 {
imgui.IO_AddInputCharacter(io, u32(e.key))
}
case k2.Event_Key_Went_Up:
imgui.IO_AddKeyEvent(io, karl2d_key_map[e.key], false)
case k2.Event_Mouse_Button_Went_Down:
imgui.IO_AddMouseButtonEvent(io, i32(e.button), true)
case k2.Event_Mouse_Button_Went_Up:
imgui.IO_AddMouseButtonEvent(io, i32(e.button), false)
case k2.Event_Mouse_Move:
imgui.IO_AddMousePosEvent(io, e.position.x, e.position.y)
case k2.Event_Mouse_Wheel:
imgui.IO_AddMouseWheelEvent(io, 0, e.delta)
case k2.Event_Window_Focused:
imgui.IO_AddFocusEvent(io, true)
case k2.Event_Window_Unfocused:
imgui.IO_AddFocusEvent(io, false)
}
}
}
@private
update_texture :: proc(tex: ^imgui.TextureData) {
switch tex.Status {
case .OK, .Destroyed:
return
case .WantCreate:
format := tex.Format == .RGBA32 ? k2.Pixel_Format.RGBA_8_Norm : k2.Pixel_Format.R_8_Norm
format_size := format == .RGBA_8_Norm ? 4 : 1
width := int(tex.Width)
height := int(tex.Height)
texture := k2.load_texture_from_bytes_raw(slice.bytes_from_ptr(tex.Pixels, width * height * format_size), width, height, format)
tex.Status = .OK
imgui.TextureData_SetTexID(tex, transmute(imgui.TextureID)texture.handle)
case .WantDestroy:
handle := transmute(k2.Texture_Handle)tex.TexID
texture := k2.Texture { handle = handle }
k2.destroy_texture(texture)
tex.Status = .Destroyed
imgui.TextureData_SetTexID(tex, {})
case .WantUpdates:
handle := transmute(k2.Texture_Handle)tex.TexID
texture := k2.Texture { handle = handle }
width := f32(tex.Width)
height := f32(tex.Height)
k2.update_texture(texture, slice.bytes_from_ptr(imgui.TextureData_GetPixels(tex), int(imgui.TextureData_GetSizeInBytes(tex))), { 0, 0, width, height })
tex.Status = .OK
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment