Forked from nickrolfe/windows_modern_opengl_context.c
Last active
January 15, 2025 03:51
-
-
Save oskarnp/eb1f220c097d18c30bcca202c45d2e0c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Sample code showing how to create a modern OpenGL window and rendering | |
// context on Win32, using Odin. | |
// | |
// Ported from https://gist.github.com/nickrolfe/1127313ed1dbf80254b614a721b3ee9c | |
package main | |
import "core:sys/windows" | |
import "core:strings" | |
import "core:fmt" | |
import "core:intrinsics" | |
import "core:c" | |
import gl "vendor:OpenGL" | |
CONTEXT_GL_MAJOR := 3 | |
CONTEXT_GL_MINOR := 3 | |
L :: intrinsics.constant_utf16_cstring | |
window_callback :: proc "system" (window: windows.HWND, | |
msg: windows.UINT, | |
wparam: windows.WPARAM, | |
lparam: windows.LPARAM) -> (result: windows.LRESULT) { | |
using windows | |
switch msg { | |
case WM_CLOSE: | |
DestroyWindow(window) | |
case WM_DESTROY: | |
PostQuitMessage(0) | |
case: | |
result = DefWindowProcW(window, msg, wparam, lparam) | |
} | |
return | |
} | |
fatal_error :: proc(msg: cstring) { | |
using windows | |
fmt.eprintln(msg) | |
MessageBoxW(nil, utf8_to_wstring(string(msg)), L("Error"), MB_OK | MB_ICONEXCLAMATION) | |
ExitProcess(1) | |
} | |
init_opengl_extensions :: proc() { | |
using windows | |
// Before we can load extensions, we need a dummy OpenGL context, created | |
// using a dummy window. We use a dummy window because you can only set | |
// the pixel format for a window once. For the real window, we want to | |
// use wglChoosePixelFormatARB (so we can potentially specify options | |
// that aren't available in PIXELFORMATDESCRIPTOR), but we can't load and | |
// use that before we have a context. | |
window_class := WNDCLASSW { | |
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, | |
lpfnWndProc = DefWindowProcW, | |
hInstance = HINSTANCE(GetModuleHandleW(nil)), | |
lpszClassName = L("Dummy_WGL_djuasiodwa"), | |
} | |
if RegisterClassW(&window_class) == 0 { | |
fatal_error("Failed to register dummy OpenGL window.") | |
} | |
dummy_window := CreateWindowExW( | |
0, | |
window_class.lpszClassName, | |
L("Dummy OpenGL Window"), | |
0, | |
CW_USEDEFAULT, | |
CW_USEDEFAULT, | |
CW_USEDEFAULT, | |
CW_USEDEFAULT, | |
nil, | |
nil, | |
window_class.hInstance, | |
nil) | |
if dummy_window == nil { | |
fatal_error("Failed to create dummy OpenGL window.") | |
} | |
dummy_dc := GetDC(dummy_window) | |
pfd := PIXELFORMATDESCRIPTOR { | |
nSize = size_of(PIXELFORMATDESCRIPTOR), | |
nVersion = 1, | |
iPixelType = PFD_TYPE_RGBA, | |
dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, | |
cColorBits = 32, | |
cAlphaBits = 8, | |
iLayerType = PFD_MAIN_PLANE, | |
cDepthBits = 24, | |
cStencilBits = 8, | |
} | |
pixel_format := ChoosePixelFormat(dummy_dc, &pfd) | |
if pixel_format == 0 { | |
fatal_error("Failed to find a suitable pixel format.") | |
} | |
if !SetPixelFormat(dummy_dc, pixel_format, &pfd) { | |
fatal_error("Failed to set the pixel format.") | |
} | |
dummy_context := wglCreateContext(dummy_dc) | |
if dummy_context == nil { | |
fatal_error("Failed to create a dummy OpenGL rendering context.") | |
} | |
if !wglMakeCurrent(dummy_dc, dummy_context) { | |
fatal_error("Failed to activate dummy OpenGL rendering context.") | |
} | |
wglCreateContextAttribsARB = cast(CreateContextAttribsARBType) wglGetProcAddress("wglCreateContextAttribsARB") | |
wglChoosePixelFormatARB = cast(ChoosePixelFormatARBType) wglGetProcAddress("wglChoosePixelFormatARB") | |
wglMakeCurrent(dummy_dc, nil) | |
wglDeleteContext(dummy_context) | |
ReleaseDC(dummy_window, dummy_dc) | |
DestroyWindow(dummy_window) | |
} | |
init_opengl :: proc(real_dc: windows.HDC) -> windows.HGLRC { | |
using windows | |
init_opengl_extensions() | |
// Now we can choose a pixel format the modern way, using | |
// wglChoosePixelFormatARB. | |
pixel_format_attribs := [?]c.int{ | |
WGL_DRAW_TO_WINDOW_ARB, 1, | |
WGL_SUPPORT_OPENGL_ARB, 1, | |
WGL_DOUBLE_BUFFER_ARB, 1, | |
WGL_ACCELERATION_ARB, windows.WGL_FULL_ACCELERATION_ARB, | |
WGL_PIXEL_TYPE_ARB, windows.WGL_TYPE_RGBA_ARB, | |
WGL_COLOR_BITS_ARB, 32, | |
WGL_DEPTH_BITS_ARB, 24, | |
WGL_STENCIL_BITS_ARB, 8, | |
0, | |
} | |
pixel_format: c.int | |
num_formats: UINT | |
wglChoosePixelFormatARB(real_dc, &pixel_format_attribs[0], nil, 1, &pixel_format, &num_formats) | |
if num_formats == 0 { | |
fatal_error("wglChoosePixelFormatARB") | |
} | |
pfd: PIXELFORMATDESCRIPTOR | |
DescribePixelFormat(real_dc, pixel_format, size_of(pfd), &pfd) | |
if !SetPixelFormat(real_dc, pixel_format, &pfd) { | |
fatal_error("SetPixelFormat") | |
} | |
gl_attribs := [?]c.int{ | |
WGL_CONTEXT_MAJOR_VERSION_ARB, c.int(CONTEXT_GL_MAJOR), | |
WGL_CONTEXT_MINOR_VERSION_ARB, c.int(CONTEXT_GL_MINOR), | |
WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, | |
0, | |
}; | |
gl_context := wglCreateContextAttribsARB(real_dc, nil, &gl_attribs[0]) | |
if gl_context == nil { | |
fatal_error("wglCreateContextAttribsARB") | |
} | |
if !wglMakeCurrent(real_dc, gl_context) { | |
fatal_error("wglMakeCurrent") | |
} | |
return gl_context | |
} | |
create_window :: proc(inst: windows.HINSTANCE) -> windows.HWND { | |
using windows | |
window_class := WNDCLASSW { | |
style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC, | |
lpfnWndProc = window_callback, | |
hInstance = inst, | |
hCursor = windows.LoadCursorW(nil, transmute([^]u16)IDC_ARROW), | |
hbrBackground = nil, | |
lpszClassName = L("WGL_fdjhsklf"), | |
} | |
if RegisterClassW(&window_class) == 0 { | |
fatal_error("Failed to register window.") | |
} | |
// Specify a desired width and height, then adjust the rect so the | |
// window's client area will be that size. | |
rect := RECT { | |
right = 1024, | |
bottom = 576, | |
}; | |
window_style: DWORD = WS_OVERLAPPEDWINDOW | |
AdjustWindowRect(&rect, window_style, false) | |
window := CreateWindowW( | |
window_class.lpszClassName, | |
L("OpenGL"), | |
window_style, | |
CW_USEDEFAULT, | |
CW_USEDEFAULT, | |
rect.right - rect.left, | |
rect.bottom - rect.top, | |
nil, | |
nil, | |
inst, | |
nil) | |
if window == nil { | |
fatal_error("Failed to create window.") | |
} | |
return window | |
} | |
main :: proc() { | |
using windows | |
window := create_window(nil) | |
gldc := GetDC(window) | |
glrc := init_opengl(gldc) | |
gl.load_up_to(CONTEXT_GL_MAJOR, CONTEXT_GL_MINOR, proc(p0: rawptr, name: cstring) { | |
p := windows.wglGetProcAddress(name) | |
if p <= rawptr(uintptr(3)) { | |
module := windows.LoadLibraryW(L("opengl32.dll")) | |
p = windows.GetProcAddress(module, name) | |
} | |
if p == nil { | |
fmt.eprintf("Failed to load GL proc: %s", name) | |
} | |
(cast(^rawptr)p0)^ = p | |
}) | |
ShowWindow(window, SW_SHOW) | |
UpdateWindow(window) | |
loop: for { | |
msg: MSG | |
for PeekMessageW(&msg, window, 0, 0, PM_REMOVE) { | |
if msg.message == WM_QUIT { | |
break loop | |
} else { | |
TranslateMessage(&msg) | |
DispatchMessageW(&msg) | |
} | |
} | |
gl.ClearColor(1.0, 0.5, 0.5, 1.0) | |
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT) | |
SwapBuffers(gldc) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment