@@ -0,0 +1,327 @@
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)
}
}