Skip to content

Instantly share code, notes, and snippets.

@GrumpyLion
Last active November 22, 2024 07:03
Show Gist options
  • Save GrumpyLion/7298282a6c7e2bcab699a72806d2ca59 to your computer and use it in GitHub Desktop.
Save GrumpyLion/7298282a6c7e2bcab699a72806d2ca59 to your computer and use it in GitHub Desktop.
OpenGL and win32 window creation with Odin
package main
// This is how you can manually initialize a window and OpenGL context on windows, instead of using SDL or GLFW.
// Also renders a colorful quad onto the screen.
// I made this as an exercise and getting into Odin.
// This is my first "complete" odin program, so there's probably much better ways of doing this :D
import "base:runtime"
import "core:fmt"
import "core:c"
import win32 "core:sys/windows"
import glm "core:math/linalg/glsl"
import gl "vendor:OpenGL"
vertex_source := `
#version 330 core
layout(location=0) in vec3 a_position;
layout(location=1) in vec4 a_color;
out vec4 v_color;
void main() {
gl_Position = vec4(a_position, 1.0);
v_color = a_color;
}
`
fragment_source := `
#version 330 core
in vec4 v_color;
out vec4 o_color;
void main() {
o_color = v_color;
}
`
wglSwapIntervalEXT : win32.SwapIntervalEXTType
running : bool = false
// is also available in core/sys/windows/wgl.odin
gl_set_proc_address :: proc(p: rawptr, name: cstring) {
func := win32.wglGetProcAddress(name)
switch uintptr(func) {
case 0, 1, 2, 3, ~uintptr(0):
module := win32.LoadLibraryW(win32.L("opengl32.dll"))
func = win32.GetProcAddress(module, name)
}
(^rawptr)(p)^ = func
}
GL_MAJOR_VERSION : c.int : 4
GL_MINOR_VERSION :: 6
CLASSNAME : cstring : "OpenGLClass"
APPNAME : cstring : "OpenGL"
WinProc :: proc "stdcall" (
window: win32.HWND,
message: win32.UINT,
wparam: win32.WPARAM,
lparam: win32.LPARAM
) -> win32.LRESULT {
result: win32.LRESULT
switch message {
case win32.WM_DESTROY:
win32.DestroyWindow(window)
win32.PostQuitMessage(0)
running = false
result = 0
case:
result = win32.DefWindowProcW( window, message, wparam, lparam)
}
return result
}
gl_debug_callback :: proc "c" (source: u32, type: u32, id: u32, severity: u32, length: i32, message: cstring, userParam: rawptr) {
context = runtime.default_context()
fmt.println(message)
}
main :: proc() {
hinst : win32.HANDLE = auto_cast win32.GetModuleHandleW(nil)
icon := win32.LoadIconA(nil, win32.IDI_APPLICATION)
cursor := win32.LoadCursorA(nil, win32.IDC_ARROW)
window_context := win32.WNDCLASSEXW {
size_of(win32.WNDCLASSEXW),
win32.CS_HREDRAW | win32.CS_VREDRAW,
WinProc,
0,0,
hinst,icon,cursor, nil, nil,
win32.L(CLASSNAME),
nil,
}
if error := win32.RegisterClassExW(&window_context); error == 0 {
win32.MessageBoxW(nil, win32.L("Unable to Register Window Class"), win32.L("Error"), win32.MB_ICONERROR)
return
}
wglChoosePixelFormatARB : win32.ChoosePixelFormatARBType
wglCreateContextAttribsARB : win32.CreateContextAttribsARBType
dw_style := win32.WS_OVERLAPPEDWINDOW
// fake window and context for acquiring functions pointers
hwnd := win32.CreateWindowExW(
win32.WS_EX_OVERLAPPEDWINDOW,
win32.L(CLASSNAME),
win32.L(APPNAME),
dw_style,
win32.CW_USEDEFAULT, win32.CW_USEDEFAULT, win32.CW_USEDEFAULT, win32.CW_USEDEFAULT,
nil, nil, hinst, nil,
)
if hwnd == nil {
win32.MessageBoxW(nil, win32.L("Unable to Create Window"),win32.L("Error"), win32.MB_ICONERROR)
return
}
fake_dc := win32.GetDC(hwnd)
if fake_dc == nil {
win32.MessageBoxW(nil, win32.L("Unable to Get DC"), win32.L("Error"), win32.MB_ICONERROR)
return
}
pfd := win32.PIXELFORMATDESCRIPTOR{
nSize = size_of(win32.PIXELFORMATDESCRIPTOR),
nVersion = 1,
dwFlags = win32.PFD_DRAW_TO_WINDOW | win32.PFD_SUPPORT_OPENGL | win32.PFD_DOUBLEBUFFER,
iPixelType = win32.PFD_TYPE_RGBA,
cColorBits = 32,
cAlphaBits = 8,
cDepthBits = 24
}
pixel_format := win32.ChoosePixelFormat(fake_dc, &pfd)
if pixel_format == 0 {
win32.MessageBoxW(nil, win32.L("Failed to get pixel format"), win32.L("Error"), win32.MB_ICONERROR)
return
}
if !win32.SetPixelFormat(fake_dc, pixel_format, &pfd) {
win32.MessageBoxW(nil, win32.L("Failed to set pixel format"), win32.L("Error"), win32.MB_ICONERROR)
return
}
fake_rc := win32.wglCreateContext(fake_dc)
if fake_rc == nil {
win32.MessageBoxW(nil, win32.L("Failed to create context"), win32.L("Error"), win32.MB_ICONERROR)
return
}
if !win32.wglMakeCurrent(fake_dc, fake_rc) {
win32.MessageBoxW(nil, win32.L("Failed to Get Pixel Format"), win32.L("Error"), win32.MB_ICONERROR)
return
}
gl_set_proc_address(&wglChoosePixelFormatARB, "wglChoosePixelFormatARB")
gl_set_proc_address(&wglCreateContextAttribsARB, "wglCreateContextAttribsARB")
gl_set_proc_address(&wglSwapIntervalEXT, "wglSwapIntervalEXT")
win32.wglMakeCurrent(fake_dc, nil)
win32.wglDeleteContext(fake_rc)
win32.ReleaseDC(hwnd, fake_dc)
win32.DestroyWindow(hwnd)
// actual window and context creation
width : i32 = 1024
height : i32 = 600
border_rect : win32.RECT = {}
win32.AdjustWindowRectEx(&border_rect, dw_style, false, 0)
width += border_rect.right - border_rect.left
height += border_rect.bottom - border_rect.top
hwnd = win32.CreateWindowExW(
win32.WS_EX_OVERLAPPEDWINDOW,
win32.L(CLASSNAME),
win32.L(APPNAME),
dw_style,
100, 100, width, height,
nil,nil,hinst,nil,
)
defer win32.DestroyWindow(hwnd)
if hwnd == nil {
win32.MessageBoxW(nil, win32.L("Unable to Create Window"), win32.L("Error"), win32.MB_ICONERROR)
return
}
dc := win32.GetDC(hwnd)
defer win32.ReleaseDC(hwnd, dc)
pixel_attribs : []i32 = {
win32.WGL_DRAW_TO_WINDOW_ARB, 1,
win32.WGL_SUPPORT_OPENGL_ARB, 1,
win32.WGL_DOUBLE_BUFFER_ARB, 1,
win32.WGL_SWAP_METHOD_ARB, win32.WGL_SWAP_COPY_ARB,
win32.WGL_PIXEL_TYPE_ARB, win32.WGL_TYPE_RGBA_ARB,
win32.WGL_ACCELERATION_ARB, win32.WGL_FULL_ACCELERATION_ARB,
win32.WGL_COLOR_BITS_ARB, 32,
win32.WGL_ALPHA_BITS_ARB, 8,
win32.WGL_DEPTH_BITS_ARB, 24,
0
}
pixel_format = 0
num_pixel_formats : win32.UINT32 = 0
if !wglChoosePixelFormatARB(dc, raw_data(pixel_attribs), nil, 1, &pixel_format, &num_pixel_formats) {
win32.MessageBoxW(nil,win32.L("Failed to choose a pixel format"), win32.L("Error"), win32.MB_ICONERROR)
return
}
pixel_format_descriptor : win32.PIXELFORMATDESCRIPTOR
win32.DescribePixelFormat(dc, pixel_format, size_of(win32.PIXELFORMATDESCRIPTOR), &pixel_format_descriptor)
if !win32.SetPixelFormat(dc, pixel_format, &pixel_format_descriptor) {
win32.MessageBoxW(nil,win32.L("Unable to set pixle format"), win32.L("Error"), win32.MB_ICONERROR)
return
}
context_attribs : []i32 = {
win32.WGL_CONTEXT_MAJOR_VERSION_ARB, GL_MAJOR_VERSION,
win32.WGL_CONTEXT_MINOR_VERSION_ARB, GL_MINOR_VERSION,
win32.WGL_CONTEXT_PROFILE_MASK_ARB, win32.WGL_CONTEXT_CORE_PROFILE_BIT_ARB,
win32.WGL_CONTEXT_FLAGS_ARB, win32.WGL_CONTEXT_DEBUG_BIT_ARB,
0
}
render_context := wglCreateContextAttribsARB(dc, nil, raw_data(context_attribs))
defer win32.wglDeleteContext(render_context)
if !win32.wglMakeCurrent(dc, render_context) {
win32.MessageBoxW(nil,win32.L("Failed to make the current context"), win32.L("Error"), win32.MB_ICONERROR)
return
}
defer win32.wglMakeCurrent(dc, nil)
win32.ShowWindow(hwnd, win32.SW_SHOWNORMAL)
// opengl init
gl.load_up_to(int(GL_MAJOR_VERSION), GL_MINOR_VERSION, gl_set_proc_address)
gl.Enable(gl.DEBUG_OUTPUT)
gl.Enable(gl.DEBUG_OUTPUT_SYNCHRONOUS)
gl.DebugMessageCallback(gl_debug_callback, nil)
gl.Viewport(0, 0, width, height)
// shader init
program, program_ok := gl.load_shaders_source(vertex_source, fragment_source)
if !program_ok {
win32.MessageBoxW(nil,win32.L("Failed to create GLSL program"), win32.L("Error"), win32.MB_ICONERROR)
return
}
defer gl.DeleteProgram(program)
gl.UseProgram(program)
uniforms := gl.get_uniforms_from_program(program)
defer delete(uniforms)
// vertex struct
Vertex :: struct {
pos: glm.vec3,
col: glm.vec4,
}
vertices := []Vertex{
{{-0.5, +0.5, 0}, {1.0, 0.0, 0.0, 0.75}},
{{-0.5, -0.5, 0}, {1.0, 1.0, 0.0, 0.75}},
{{+0.5, -0.5, 0}, {0.0, 1.0, 0.0, 0.75}},
{{+0.5, +0.5, 0}, {0.0, 0.0, 1.0, 0.75}},
}
indices := []u16{
0, 1, 2,
2, 3, 0,
}
// buffers init
vao: u32
gl.CreateVertexArrays(1, &vao); defer gl.DeleteVertexArrays(1, &vao)
vbo, ebo: u32
gl.CreateBuffers(1, &vbo); defer gl.DeleteBuffers(1, &vbo)
gl.CreateBuffers(1, &ebo); defer gl.DeleteBuffers(1, &ebo)
gl.NamedBufferData(vbo, len(vertices) * size_of(Vertex), raw_data(vertices), gl.STATIC_DRAW)
gl.NamedBufferData(ebo, len(indices) * size_of(u16), raw_data(indices), gl.STATIC_DRAW)
gl.EnableVertexArrayAttrib(vao, 0)
gl.VertexArrayAttribBinding(vao, 0, 0)
gl.VertexArrayAttribFormat(vao, 0, 3, gl.FLOAT, false, 0)
gl.EnableVertexArrayAttrib(vao, 1)
gl.VertexArrayAttribBinding(vao, 1, 0)
gl.VertexArrayAttribFormat(vao, 1, 4, gl.FLOAT, false, 3 * size_of(f32))
gl.VertexArrayVertexBuffer(vao, 0, vbo, 0, size_of(Vertex))
gl.VertexArrayElementBuffer(vao, ebo)
msg : win32.MSG
running = true
for running {
// window handling
for win32.PeekMessageA(&msg, hwnd, 0, 0, win32.PM_REMOVE) {
win32.TranslateMessage(&msg)
win32.DispatchMessageW(&msg)
}
// draw
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gl.ClearColor(0.5, 0.5, 0.5, 1.0)
gl.BindVertexArray(vao)
gl.UseProgram(program)
gl.DrawElements(gl.TRIANGLES, i32(len(indices)), gl.UNSIGNED_SHORT, nil)
win32.SwapBuffers(dc)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment