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) } }