Skip to content

Instantly share code, notes, and snippets.

@lucaspoffo
Last active March 30, 2026 18:57
Show Gist options
  • Select an option

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

Select an option

Save lucaspoffo/a0d4192acd74d718e433ea0bafe17bc4 to your computer and use it in GitHub Desktop.
Raylib backend for Dear ImGui for the odin bindings
package imgui_impl_raylib
// Based on the raylib extras rlImGui: https://github.com/raylib-extras/rlImGui/blob/main/rlImGui.cpp
/* Usage:
import imgui_rl "imgui_impl_raylib"
import imgui "../../odin-imgui"
main :: proc() {
rl.SetConfigFlags({ rl.ConfigFlag.WINDOW_RESIZABLE })
rl.InitWindow(800, 600, "raylib basic window")
defer rl.CloseWindow()
imgui.CreateContext(nil)
defer imgui.DestroyContext(nil)
imgui_rl.init()
defer imgui_rl.shutdown()
imgui_rl.build_font_atlas()
for !rl.WindowShouldClose() {
imgui_rl.process_events()
imgui_rl.new_frame()
imgui.NewFrame()
rl.BeginDrawing()
rl.ClearBackground(rl.RAYWHITE)
imgui.ShowDemoWindow(nil)
imgui.Render()
imgui_rl.render_draw_data(imgui.GetDrawData())
rl.EndDrawing()
}
}
*/
import "core:c"
import "core:mem"
import "core:math"
// Follow build instruction for imgui bindings in: https://gitlab.com/L-4/odin-imgui
import imgui "../../../odin-imgui"
import rl "vendor:raylib"
current_mouse_cursor: imgui.MouseCursor = imgui.MouseCursor.COUNT
mouse_cursor_map: [imgui.MouseCursor.COUNT]rl.MouseCursor
last_frame_focused := false
last_control_pressed := false
last_shift_pressed := false
last_alt_pressed := false
last_super_pressed := false
raylib_key_map: map[rl.KeyboardKey]imgui.Key = {}
init :: proc() -> bool {
setup_globals()
setup_keymap()
setup_mouse_cursor()
setup_backend()
return true
}
build_font_atlas :: proc() -> mem.Allocator_Error {
io: ^imgui.IO = imgui.GetIO()
pixels: ^c.uchar
width, height: c.int
imgui.FontAtlas_GetTexDataAsRGBA32(io.Fonts, &pixels, &width, &height, nil)
image: rl.Image = rl.GenImageColor(width, height, rl.BLANK)
mem.copy(image.data, pixels, int(width * height * 4))
font_texture: ^rl.Texture2D = transmute(^rl.Texture)io.Fonts.TexID
if font_texture != nil && font_texture.id != 0 {
rl.UnloadTexture(font_texture^)
mem.free(font_texture)
}
font_texture = cast(^rl.Texture2D)(mem.alloc(size_of(rl.Texture2D), align_of(rl.Texture2D)) or_return)
font_texture^ = rl.LoadTextureFromImage(image)
rl.UnloadImage(image)
io.Fonts.TexID = font_texture
return nil
}
shutdown :: proc() {
io: ^imgui.IO = imgui.GetIO()
font_texture: ^rl.Texture2D = transmute(^rl.Texture)io.Fonts.TexID
if font_texture != nil && font_texture.id != 0 {
rl.UnloadTexture(font_texture^)
mem.free(font_texture)
}
io.Fonts.TexID = nil
}
new_frame :: proc() {
io: ^imgui.IO = imgui.GetIO()
if rl.IsWindowFullscreen() {
monitor := rl.GetCurrentMonitor()
io.DisplaySize.x = f32(rl.GetMonitorWidth(monitor))
io.DisplaySize.y = f32(rl.GetMonitorHeight(monitor))
} else {
io.DisplaySize.x = f32(rl.GetScreenWidth())
io.DisplaySize.y = f32(rl.GetScreenHeight())
}
io.DisplayFramebufferScale = rl.GetWindowScaleDPI()
io.DeltaTime = rl.GetFrameTime()
if io.WantSetMousePos {
rl.SetMousePosition(c.int(io.MousePos.x), c.int(io.MousePos.y))
} else {
mouse_pos := rl.GetMousePosition()
imgui.IO_AddMousePosEvent(io, mouse_pos.x, mouse_pos.y)
}
set_mouse_event :: proc(io: ^imgui.IO, rl_mouse: rl.MouseButton, imgui_mouse: c.int) {
if rl.IsMouseButtonPressed(rl_mouse) {
imgui.IO_AddMouseButtonEvent(io, imgui_mouse, true)
} else if rl.IsMouseButtonReleased(rl_mouse) {
imgui.IO_AddMouseButtonEvent(io, imgui_mouse, false)
}
}
set_mouse_event(io, rl.MouseButton.LEFT, c.int(imgui.MouseButton.Left))
set_mouse_event(io, rl.MouseButton.RIGHT, c.int(imgui.MouseButton.Right))
set_mouse_event(io, rl.MouseButton.MIDDLE, c.int(imgui.MouseButton.Middle))
set_mouse_event(io, rl.MouseButton.FORWARD, c.int(imgui.MouseButton.Middle) + 1)
set_mouse_event(io, rl.MouseButton.BACK, c.int(imgui.MouseButton.Middle) + 2)
mouse_wheel := rl.GetMouseWheelMoveV()
imgui.IO_AddMouseWheelEvent(io, mouse_wheel.x, mouse_wheel.y)
if imgui.ConfigFlag.NoMouseCursorChange not_in io.ConfigFlags {
imgui_cursor: imgui.MouseCursor = imgui.GetMouseCursor()
if imgui_cursor != current_mouse_cursor || io.MouseDrawCursor {
current_mouse_cursor = imgui_cursor
if io.MouseDrawCursor || imgui_cursor == imgui.MouseCursor.None {
rl.HideCursor()
} else {
rl.ShowCursor()
if c.int(imgui_cursor) > -1 && imgui_cursor < imgui.MouseCursor.COUNT {
rl.SetMouseCursor(mouse_cursor_map[imgui_cursor])
} else {
rl.SetMouseCursor(rl.MouseCursor.DEFAULT)
}
}
}
}
}
render_draw_data :: proc(draw_data: ^imgui.DrawData) {
rl.rlDrawRenderBatchActive()
rl.rlDisableBackfaceCulling()
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]
enable_scissor(
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
}
render_triangles(cmd.ElemCount, cmd.IdxOffset, command_list.IdxBuffer, command_list.VtxBuffer, cmd.TextureId)
rl.rlDrawRenderBatchActive()
}
}
rl.rlSetTexture(0)
rl.rlDisableScissorTest()
rl.rlEnableBackfaceCulling()
}
@private
enable_scissor :: proc(x: f32, y: f32, width: f32, height: f32) {
rl.rlEnableScissorTest()
io: ^imgui.IO = imgui.GetIO()
rl.rlScissor(
i32(x * io.DisplayFramebufferScale.x),
i32((io.DisplaySize.y - math.floor(y + height)) * io.DisplayFramebufferScale.y),
i32(width * io.DisplayFramebufferScale.x),
i32(height * io.DisplayFramebufferScale.y)
)
}
@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: ^rl.Texture = transmute(^rl.Texture)texture_ptr
texture_id: u32 = (texture == nil) ? 0 : texture.id
rl.rlBegin(rl.RL_TRIANGLES)
rl.rlSetTexture(texture_id)
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 {
if rl.rlCheckRenderBatchLimit(3) != 0 {
rl.rlBegin(rl.RL_TRIANGLES)
rl.rlSetTexture(texture_id)
}
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]
draw_triangle_vert :: proc(vert: imgui.DrawVert) {
c: rl.Color = transmute(rl.Color)vert.col
rl.rlColor4ub(c.r, c.g, c.b, c.a)
rl.rlTexCoord2f(vert.uv.x, vert.uv.y)
rl.rlVertex2f(vert.pos.x, vert.pos.y)
}
draw_triangle_vert(vertex_a)
draw_triangle_vert(vertex_b)
draw_triangle_vert(vertex_c)
}
rl.rlEnd()
}
is_control_down :: proc() -> bool { return rl.IsKeyDown(rl.KeyboardKey.RIGHT_CONTROL) || rl.IsKeyDown(rl.KeyboardKey.LEFT_CONTROL); }
is_shift_down :: proc() -> bool { return rl.IsKeyDown(rl.KeyboardKey.RIGHT_SHIFT) || rl.IsKeyDown(rl.KeyboardKey.LEFT_SHIFT); }
is_alt_down :: proc() -> bool { return rl.IsKeyDown(rl.KeyboardKey.RIGHT_ALT) || rl.IsKeyDown(rl.KeyboardKey.LEFT_ALT); }
is_super_down :: proc() -> bool { return rl.IsKeyDown(rl.KeyboardKey.RIGHT_SUPER) || rl.IsKeyDown(rl.KeyboardKey.LEFT_SUPER); }
process_events :: proc() -> bool {
io: ^imgui.IO = imgui.GetIO()
focused := rl.IsWindowFocused()
if (focused != last_frame_focused) {
imgui.IO_AddFocusEvent(io, focused)
}
last_frame_focused = focused
// Handle modifers for key evets so that shortcuts work
ctrl_down := is_control_down()
if ctrl_down != last_control_pressed {
imgui.IO_AddKeyEvent(io, imgui.Key.ImGuiMod_Ctrl, ctrl_down)
}
last_control_pressed = ctrl_down
shift_down := is_shift_down()
if shift_down != last_shift_pressed {
imgui.IO_AddKeyEvent(io, imgui.Key.ImGuiMod_Shift, shift_down)
}
last_shift_pressed = shift_down
alt_down := is_alt_down()
if alt_down != last_alt_pressed {
imgui.IO_AddKeyEvent(io, imgui.Key.ImGuiMod_Alt, alt_down)
}
last_alt_pressed = alt_down
super_down := is_super_down()
if super_down != last_super_pressed {
imgui.IO_AddKeyEvent(io, imgui.Key.ImGuiMod_Super, super_down)
}
last_super_pressed = super_down
// Get pressed keys, they are in event order
key_id: rl.KeyboardKey = rl.GetKeyPressed()
for key_id != rl.KeyboardKey.KEY_NULL {
key, ok := raylib_key_map[key_id]
if ok {
imgui.IO_AddKeyEvent(io, key, true)
}
key_id = rl.GetKeyPressed()
}
// Check for released keys
for key in raylib_key_map {
if rl.IsKeyReleased(key) {
imgui.IO_AddKeyEvent(io, raylib_key_map[key], false)
}
}
// Add text input in order
pressed: rune = rl.GetCharPressed()
for pressed != 0 {
imgui.IO_AddInputCharacter(io, u32(pressed))
pressed = rl.GetCharPressed()
}
return true
}
@private
setup_globals :: proc() {
last_frame_focused = rl.IsWindowFocused()
last_control_pressed = false
last_shift_pressed = false
last_alt_pressed = false
last_super_pressed = false
}
@private
setup_keymap :: proc() {
raylib_key_map[rl.KeyboardKey.APOSTROPHE] = imgui.Key.Apostrophe
raylib_key_map[rl.KeyboardKey.COMMA] = imgui.Key.Comma
raylib_key_map[rl.KeyboardKey.MINUS] = imgui.Key.Minus
raylib_key_map[rl.KeyboardKey.PERIOD] = imgui.Key.Period
raylib_key_map[rl.KeyboardKey.SLASH] = imgui.Key.Slash
raylib_key_map[rl.KeyboardKey.ZERO] = imgui.Key._0
raylib_key_map[rl.KeyboardKey.ONE] = imgui.Key._1
raylib_key_map[rl.KeyboardKey.TWO] = imgui.Key._2
raylib_key_map[rl.KeyboardKey.THREE] = imgui.Key._3
raylib_key_map[rl.KeyboardKey.FOUR] = imgui.Key._4
raylib_key_map[rl.KeyboardKey.FIVE] = imgui.Key._5
raylib_key_map[rl.KeyboardKey.SIX] = imgui.Key._6
raylib_key_map[rl.KeyboardKey.SEVEN] = imgui.Key._7
raylib_key_map[rl.KeyboardKey.EIGHT] = imgui.Key._8
raylib_key_map[rl.KeyboardKey.NINE] = imgui.Key._9
raylib_key_map[rl.KeyboardKey.SEMICOLON] = imgui.Key.Semicolon
raylib_key_map[rl.KeyboardKey.EQUAL] = imgui.Key.Equal
raylib_key_map[rl.KeyboardKey.A] = imgui.Key.A
raylib_key_map[rl.KeyboardKey.B] = imgui.Key.B
raylib_key_map[rl.KeyboardKey.C] = imgui.Key.C
raylib_key_map[rl.KeyboardKey.D] = imgui.Key.D
raylib_key_map[rl.KeyboardKey.E] = imgui.Key.E
raylib_key_map[rl.KeyboardKey.F] = imgui.Key.F
raylib_key_map[rl.KeyboardKey.G] = imgui.Key.G
raylib_key_map[rl.KeyboardKey.H] = imgui.Key.H
raylib_key_map[rl.KeyboardKey.I] = imgui.Key.I
raylib_key_map[rl.KeyboardKey.J] = imgui.Key.J
raylib_key_map[rl.KeyboardKey.K] = imgui.Key.K
raylib_key_map[rl.KeyboardKey.L] = imgui.Key.L
raylib_key_map[rl.KeyboardKey.M] = imgui.Key.M
raylib_key_map[rl.KeyboardKey.N] = imgui.Key.N
raylib_key_map[rl.KeyboardKey.O] = imgui.Key.O
raylib_key_map[rl.KeyboardKey.P] = imgui.Key.P
raylib_key_map[rl.KeyboardKey.Q] = imgui.Key.Q
raylib_key_map[rl.KeyboardKey.R] = imgui.Key.R
raylib_key_map[rl.KeyboardKey.S] = imgui.Key.S
raylib_key_map[rl.KeyboardKey.T] = imgui.Key.T
raylib_key_map[rl.KeyboardKey.U] = imgui.Key.U
raylib_key_map[rl.KeyboardKey.V] = imgui.Key.V
raylib_key_map[rl.KeyboardKey.W] = imgui.Key.W
raylib_key_map[rl.KeyboardKey.X] = imgui.Key.X
raylib_key_map[rl.KeyboardKey.Y] = imgui.Key.Y
raylib_key_map[rl.KeyboardKey.Z] = imgui.Key.Z
raylib_key_map[rl.KeyboardKey.SPACE] = imgui.Key.Space
raylib_key_map[rl.KeyboardKey.ESCAPE] = imgui.Key.Escape
raylib_key_map[rl.KeyboardKey.ENTER] = imgui.Key.Enter
raylib_key_map[rl.KeyboardKey.TAB] = imgui.Key.Tab
raylib_key_map[rl.KeyboardKey.BACKSPACE] = imgui.Key.Backspace
raylib_key_map[rl.KeyboardKey.INSERT] = imgui.Key.Insert
raylib_key_map[rl.KeyboardKey.DELETE] = imgui.Key.Delete
raylib_key_map[rl.KeyboardKey.RIGHT] = imgui.Key.RightArrow
raylib_key_map[rl.KeyboardKey.LEFT] = imgui.Key.LeftArrow
raylib_key_map[rl.KeyboardKey.DOWN] = imgui.Key.DownArrow
raylib_key_map[rl.KeyboardKey.UP] = imgui.Key.UpArrow
raylib_key_map[rl.KeyboardKey.PAGE_UP] = imgui.Key.PageUp
raylib_key_map[rl.KeyboardKey.PAGE_DOWN] = imgui.Key.PageDown
raylib_key_map[rl.KeyboardKey.HOME] = imgui.Key.Home
raylib_key_map[rl.KeyboardKey.END] = imgui.Key.End
raylib_key_map[rl.KeyboardKey.CAPS_LOCK] = imgui.Key.CapsLock
raylib_key_map[rl.KeyboardKey.SCROLL_LOCK] = imgui.Key.ScrollLock
raylib_key_map[rl.KeyboardKey.NUM_LOCK] = imgui.Key.NumLock
raylib_key_map[rl.KeyboardKey.PRINT_SCREEN] = imgui.Key.PrintScreen
raylib_key_map[rl.KeyboardKey.PAUSE] = imgui.Key.Pause
raylib_key_map[rl.KeyboardKey.F1] = imgui.Key.F1
raylib_key_map[rl.KeyboardKey.F2] = imgui.Key.F2
raylib_key_map[rl.KeyboardKey.F3] = imgui.Key.F3
raylib_key_map[rl.KeyboardKey.F4] = imgui.Key.F4
raylib_key_map[rl.KeyboardKey.F5] = imgui.Key.F5
raylib_key_map[rl.KeyboardKey.F6] = imgui.Key.F6
raylib_key_map[rl.KeyboardKey.F7] = imgui.Key.F7
raylib_key_map[rl.KeyboardKey.F8] = imgui.Key.F8
raylib_key_map[rl.KeyboardKey.F9] = imgui.Key.F9
raylib_key_map[rl.KeyboardKey.F10] = imgui.Key.F10
raylib_key_map[rl.KeyboardKey.F11] = imgui.Key.F11
raylib_key_map[rl.KeyboardKey.F12] = imgui.Key.F12
raylib_key_map[rl.KeyboardKey.LEFT_SHIFT] = imgui.Key.LeftShift
raylib_key_map[rl.KeyboardKey.LEFT_CONTROL] = imgui.Key.LeftCtrl
raylib_key_map[rl.KeyboardKey.LEFT_ALT] = imgui.Key.LeftAlt
raylib_key_map[rl.KeyboardKey.LEFT_SUPER] = imgui.Key.LeftSuper
raylib_key_map[rl.KeyboardKey.RIGHT_SHIFT] = imgui.Key.RightShift
raylib_key_map[rl.KeyboardKey.RIGHT_CONTROL] = imgui.Key.RightCtrl
raylib_key_map[rl.KeyboardKey.RIGHT_ALT] = imgui.Key.RightAlt
raylib_key_map[rl.KeyboardKey.RIGHT_SUPER] = imgui.Key.RightSuper
raylib_key_map[rl.KeyboardKey.KB_MENU] = imgui.Key.Menu
raylib_key_map[rl.KeyboardKey.LEFT_BRACKET] = imgui.Key.LeftBracket
raylib_key_map[rl.KeyboardKey.BACKSLASH] = imgui.Key.Backslash
raylib_key_map[rl.KeyboardKey.RIGHT_BRACKET] = imgui.Key.RightBracket
raylib_key_map[rl.KeyboardKey.GRAVE] = imgui.Key.GraveAccent
raylib_key_map[rl.KeyboardKey.KP_0] = imgui.Key.Keypad0
raylib_key_map[rl.KeyboardKey.KP_1] = imgui.Key.Keypad1
raylib_key_map[rl.KeyboardKey.KP_2] = imgui.Key.Keypad2
raylib_key_map[rl.KeyboardKey.KP_3] = imgui.Key.Keypad3
raylib_key_map[rl.KeyboardKey.KP_4] = imgui.Key.Keypad4
raylib_key_map[rl.KeyboardKey.KP_5] = imgui.Key.Keypad5
raylib_key_map[rl.KeyboardKey.KP_6] = imgui.Key.Keypad6
raylib_key_map[rl.KeyboardKey.KP_7] = imgui.Key.Keypad7
raylib_key_map[rl.KeyboardKey.KP_8] = imgui.Key.Keypad8
raylib_key_map[rl.KeyboardKey.KP_9] = imgui.Key.Keypad9
raylib_key_map[rl.KeyboardKey.KP_DECIMAL] = imgui.Key.KeypadDecimal
raylib_key_map[rl.KeyboardKey.KP_DIVIDE] = imgui.Key.KeypadDivide
raylib_key_map[rl.KeyboardKey.KP_MULTIPLY] = imgui.Key.KeypadMultiply
raylib_key_map[rl.KeyboardKey.KP_SUBTRACT] = imgui.Key.KeypadSubtract
raylib_key_map[rl.KeyboardKey.KP_ADD] = imgui.Key.KeypadAdd
raylib_key_map[rl.KeyboardKey.KP_ENTER] = imgui.Key.KeypadEnter
raylib_key_map[rl.KeyboardKey.KP_EQUAL] = imgui.Key.KeypadEqual
}
@private
setup_mouse_cursor :: proc() {
mouse_cursor_map[imgui.MouseCursor.Arrow] = rl.MouseCursor.ARROW;
mouse_cursor_map[imgui.MouseCursor.TextInput] = rl.MouseCursor.IBEAM;
mouse_cursor_map[imgui.MouseCursor.Hand] = rl.MouseCursor.POINTING_HAND;
mouse_cursor_map[imgui.MouseCursor.ResizeAll] = rl.MouseCursor.RESIZE_ALL;
mouse_cursor_map[imgui.MouseCursor.ResizeEW] = rl.MouseCursor.RESIZE_EW;
mouse_cursor_map[imgui.MouseCursor.ResizeNESW] = rl.MouseCursor.RESIZE_NESW;
mouse_cursor_map[imgui.MouseCursor.ResizeNS] = rl.MouseCursor.RESIZE_NS;
mouse_cursor_map[imgui.MouseCursor.ResizeNWSE] = rl.MouseCursor.RESIZE_NWSE;
mouse_cursor_map[imgui.MouseCursor.NotAllowed] = rl.MouseCursor.NOT_ALLOWED;
}
@private
setup_backend :: proc() {
io: ^imgui.IO = imgui.GetIO()
io.BackendPlatformName = "imgui_impl_raylib"
io.BackendFlags |= { imgui.BackendFlag.HasMouseCursors }
io.MousePos = { 0, 0 }
io.SetClipboardTextFn = set_clip_text_callback
io.GetClipboardTextFn = get_clip_text_callback
io.ClipboardUserData = nil
}
@private
set_clip_text_callback :: proc "c" (user_data: rawptr, text: cstring) {
rl.SetClipboardText(text)
}
@private
get_clip_text_callback :: proc "c" (user_data: rawptr) -> cstring {
return rl.GetClipboardText()
}
@sumofat
Copy link
Copy Markdown

sumofat commented Jan 10, 2024

I wish you would have put this out two days earlier lol. Nice one!

@Lucky44x
Copy link
Copy Markdown

Lucky44x commented Jul 8, 2024

NOTE for using this with odin 2024-07 or later, all OpenGL related functions have been moved from "vendor:raylib" to "vendor:raylib/rlgl"

@XenoBenn
Copy link
Copy Markdown

XenoBenn commented Sep 11, 2024

NOTE for using this with odin 2024-07 or later, all OpenGL related functions have been moved from "vendor:raylib" to "vendor:raylib/rlgl"

What does this mean exactly? Should I change anything in the code above?
Edit: Never mind, found your changes. Thanks.

@bilouge
Copy link
Copy Markdown

bilouge commented Mar 30, 2026

^For those wondering: yes. If you have an LSP configured, your IDE will essentially tell you where to make the change.
So after you import rlgl "vendor:raylib/rlgl", just go wherever you get your red squiggly lines in your IDE and modify it. For instance, rl.rlDrawRenderBatchActive() becomes rlgl.DrawRenderBatchActive()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment